在 JavaScript 中循环一组元素的最佳方式是啥?

Posted

技术标签:

【中文标题】在 JavaScript 中循环一组元素的最佳方式是啥?【英文标题】:What's the best way to loop through a set of elements in JavaScript?在 JavaScript 中循环一组元素的最佳方式是什么? 【发布时间】:2010-09-14 12:13:54 【问题描述】:

在过去和我目前的大多数项目中,我倾向于使用这样的 for 循环:

var elements = document.getElementsByTagName('div');
for (var i=0; i<elements.length; i++) 
    doSomething(elements[i]);

我听说使用“反向 while”循环更快,但我没有真正的方法来确认这一点:

var elements = document.getElementsByTagName('div'), 
    length = elements.length;

while(length--) 
    doSomething(elements[length]);

在循环遍历 javascript 中的元素或任何数组时,什么被认为是最佳实践?

【问题讨论】:

如果您将其中一个答案标记为已回答,那就太好了。这毕竟是 SO 的主要观点之一 :) 如果你现在重新选择一个接受的答案,那也很好,因为接受的答案一文不值>:)。 . .再说一次,这真的很旧,所以我不在乎。 【参考方案1】:

这是我经常使用的一种很好的循环形式。您从 for 语句创建迭代变量,并且不需要检查长度属性,这在迭代 NodeList 时特别昂贵。但是,您必须小心,如果数组中的任何值可能是“假的”,您就不能使用它。在实践中,我只在迭代不包含空值的对象数组(如 NodeList)时使用它。但我喜欢它的语法糖。

var list = [a:1,b:2, a:3,b:5, a:8,b:2, a:4,b:1, a:0,b:8];

for (var i=0, item; item = list[i]; i++) 
  // Look no need to do list[i] in the body of the loop
  console.log("Looping: index ", i, "item" + item);

请注意,这也可以用于向后循环(只要您的列表没有 ['-1'] 属性)

var list = [a:1,b:2, a:3,b:5, a:8,b:2, a:4,b:1, a:0,b:8];
    
for (var i = list.length - 1, item; item = list[i]; i--) 
  console.log("Looping: index ", i, "item", item);

ES6 更新

for...of 提供名称但不提供索引,自 ES6 起可用

for (const item of list) 
    console.log("Looping: index ", "Sorry!!!", "item" + item);

【讨论】:

我非常喜欢这个。起初我想知道循环将如何退出,然后我记得声明的“中间”基本上是一段时间,并且在评估为 false 时将退出。为变量分配一个不存在的数组索引 == false!很聪明。 @sudopeople 只是为了 100% 准确,当项目不存在时,它返回 undefined 这是假的。 Drats,没看到这个!对不起,被骗了!【参考方案2】:

请注意,在某些情况下,您需要以相反的顺序循环(但您也可以使用 i--)。

例如,有人想使用新的getElementsByClassName 函数循环给定类的元素并更改该类。他发现只有二分之一的元素被改变了(在 FF3 中)。 这是因为该函数返回一个活动的 NodeList,从而反映了 Dom 树中的变化。以相反的顺序遍历列表可避免此问题。

var menus = document.getElementsByClassName("style2");
for (var i = menus.length - 1; i >= 0; i--)

  menus[i].className = "style1";

在增加索引进程中,当我们询问索引 1 时,FF 会检查 Dom 并跳过第一个具有 style2 的项,即原始 Dom 的第 2 个,因此它返回第 3 个初始项!

【讨论】:

好点,但我不建议即时更改 className,因为它会强制浏览器重新计算整个文档的呈现... @roenving - 从理论上讲,您可以将整个文档复制到内存中,更改您想要的任何内容,然后用修改后的新文档替换整个文档。但要视情况而定。【参考方案3】:

我喜欢做:

 
var menu = document.getElementsByTagName('div');
for (var i = 0; menu[i]; i++) 
     ...

每次迭代都不会调用数组的长度。

【讨论】:

一个非常快速的测试(直接在我的编辑器浏览器中)表明,一千次迭代不可能告诉时间,你的方案说 15 毫秒,原始测试每次迭代的长度这是 31 毫秒 ... 请确保不要在 null、undefined、false、0,"" 是有效元素的数组上开始使用它!【参考方案4】:

我之前在使用 document.getElementsByClassName() 时遇到了非常相似的问题。我当时不知道节点列表是什么。

var elements = document.getElementsByTagName('div');
for (var i=0; i<elements.length; i++) 
    doSomething(elements[i]);

我的问题是我希望元素是一个数组,但事实并非如此。返回的节点列表 Document.getElementsByTagName() 是可迭代的,但不能对其调用 array.prototype 方法。

可以但是用这样的节点列表元素填充一个数组:

var myElements = [];
for (var i=0; i<myNodeList.length; i++)                                
    var element = myNodeList[i];
    myElements.push(element);
;

之后,您可以随意在数组元素上调用 .innerhtml 或 .style 或其他内容。

【讨论】:

【参考方案5】:

冒着被骂的风险,我会得到一个像 jquery 或 prototype 这样的 JavaScript 帮助程序库,它们将逻辑封装在很好的方法中 - 都有一个 .each 方法/迭代器来做这件事 - 他们都在努力使其跨浏览器兼容

编辑:这个答案发布于 2008 年。今天存在更好的结构。这种特殊情况可以通过.forEach 解决。

【讨论】:

为什么要使用库来执行简单的任务?-) 我知道,我知道。我只是喜欢包含 jquery 以尽量减少引入跨浏览器不兼容问题的风险。您不需要在“doSomething”中编写大量代码即可引入其中一个错误。 老实说,jQuery 只是为了处理循环?精神错乱。 @Tim:实际上我认为我会使用 javascript 创建的网站很少,在哪里添加 jquery 会很疯狂?与不使用 jquery 相比,使用 jquery 有哪些并发症? 这个问题写的时候可能已经OK了。它现在已经过时了,因为大多数浏览器(IE9+)都支持Array.prototype.forEach【参考方案6】:

我认为使用第一种形式可能是要走的路,因为它可能是迄今为止已知宇宙中最常见的循环结构,而且我不相信反向循环在现实中可以节省您的任何时间(仍在做每次迭代的增量/减量和比较)。

其他人可识别和可读的代码绝对是一件好事。

【讨论】:

【参考方案7】:

我也建议使用简单的方法(KISS!-)

--但是可以找到一些优化,即不要多次测试数组的长度:

var elements = document.getElementsByTagName('div');
for (var i=0, im=elements.length; im>i; i++) 
    doSomething(elements[i]);

【讨论】:

实际上是一个不错的改进......从现在开始肯定会在我所有的“for”循环中使用它!谢谢! 除非在 doSomething() 中发生的事情会改变数组中的元素总数。【参考方案8】:

另请参阅我对 Andrew Hedges 测试的评论...

我只是尝试运行一个测试来比较一个简单的迭代、我引入的优化和反向 do/while,其中数组中的元素在每个循环中都进行了测试。

唉,不足为奇的是,我测试的三个浏览器的结果非常不同,尽管优化后的简单迭代是最快的!-)

测试:

在实际测试之外构建一个包含 500,000 个元素的数组,每次迭代都会显示特定数组元素的值。

测试运行 10 次。

IE6:

结果:

简单:984,922,937,984,891,907,906,891,906,906

平均:923.40 毫秒。

优化:766,766,844,797,750,750,765,765,766,766

平均:773.50 毫秒。

反向do/while:3375,1328,1516,1344,1375,1406,1688,1344,1297,1265

平均:1593.80 毫秒。 (注意一个特别尴尬的结果)

歌剧 9.52:

结果:

简单:344,343,344,359,343,359,344,359,359,359

平均:351.30 毫秒。

优化:281,297,297,297,297,281,281,297,281,281

平均:289.00 毫秒

反向do/while:391,407,391,391,500,407,407,406,406,406

平均:411.20 毫秒。

火狐 3.0.1:

结果:

简单:278,251,259,245,243,242,259,246,247,256

平均:252.60 毫秒。

优化:267,222,223,226,223,230,221,231,224,230

平均:229.70 毫秒。

反向do/while:414,381,389,383,388,389,381,387,400,379

平均:389.10 毫秒。

【讨论】:

【参考方案9】:

Juan Mendez提供的循环形式非常实用, 我对其进行了一些更改,现在它也适用于 - false、null、零和空字符串。

var items = [
    true,
    false,
    null,
    0,
    ""
];

for(var i = 0, item; (item = items[i]) !== undefined; i++)

    console.log("Index: " + i + "; Value: " + item);

【讨论】:

【参考方案10】:

我知道这个问题很老了——但这是另一个非常简单的解决方案……

var elements = Array.from(document.querySelectorAll("div"));

然后它可以像任何标准数组一样使用。

【讨论】:

我需要我的朋友!【参考方案11】:

我知道你不想听到这个,但是:我认为在这种情况下最佳做法是最易读的。只要循环不从这里算到月球,性能增益就不够。

【讨论】:

同意。如果你倒数,写一个评论说“它只是一个速度问题”,那么任何想要编辑你的代码的人都不会被它弄糊涂。【参考方案12】:

我更喜欢 for 循环,因为它更具可读性。从长度循环到 0 比从 0 循环到长度更有效。正如您所说,使用反向 while 循环比 foo 循环更有效。我不再有指向具有比较结果的页面的链接,但我记得不同浏览器的差异有所不同。对于某些浏览器,反向 while 循环的速度是原来的两倍。但是,如果您循环“小”数组,则没有区别。在您的示例中,元素的长度将是“小”

【讨论】:

为什么从 length 循环到 0 比从 0 循环到 length 效率更高?【参考方案13】:

我认为您有两种选择。对于 dom 元素,如 jQuery 和类似框架,为您提供了一种很好的迭代方法。第二种方法是 for 循环。

【讨论】:

【参考方案14】:

如果元素集是根节点的子节点,我喜欢使用TreeWalker。

【讨论】:

以上是关于在 JavaScript 中循环一组元素的最佳方式是啥?的主要内容,如果未能解决你的问题,请参考以下文章

0129 JavaScript 数组:概念创建数组获取数组中的元素

JavaScript——数组&函数

使用javascript在特定元素之后插入div的最佳方法[重复]

打破 JavaScript 中嵌套循环的最佳方法是啥?

使用 jQuery/Javascript 创建集合中所有元素的列表

JavaScript数组