在JavaScript中循环一组元素的最佳方式是什么?
在过去和大多数我目前的项目中,我倾向于使用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中的元素或任何数组的循环?
这是我经常使用的一个很好的循环forms。 您可以从for语句创build迭代variables,并且不需要检查长度属性,特别是在遍历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); }
请注意,在某些情况下,您需要以相反的顺序循环(但也可以使用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的第二个项目,因此它返回第三个初始项目!
冒着吼叫的风险,我会得到一个像jQuery或原型的JavaScript帮助器库,他们封装好的方法的逻辑 – 都有一个.each方法/迭代器来做到这一点 – 他们都努力使其跨浏览器兼容
我喜欢做:
var menu = document.getElementsByTagName('div'); for (var i = 0; menu[i]; i++) { ... }
每次迭代都没有调用数组的长度。
我认为使用第一种forms可能是一种可行的方法,因为它可能是迄今为止已知宇宙中最常用的循环结构,并且由于我不相信反向循环可以节省您现实中的任何时间(仍在执行增量/递减和每次迭代的比较)。
对别人来说可识别和可读的代码绝对是一件好事。
我也build议使用简单的方法(KISS! – )
– 但是可以find一些优化,即不要多次testing一个数组的长度:
var elements = document.getElementsByTagName('div'); for (var i=0, im=elements.length; im>i; i++) { doSomething(elements[i]); }
另见我对安德鲁·赫奇斯的testing的评论…
我只是试图运行一个testing来比较一个简单的迭代,我介绍的优化和反向做/ while,其中在每个循环testing数组中的元素。
唉,毫不奇怪,我testing的三款浏览器都有不同的结果,虽然优化的简单迭代是最快的!
testing:
一个包含500,000个元素的数组可以在真正的testing之外构build,每次迭代都会显示特定数组元素的值。
试运行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毫秒。
反向做/同时:3375,1328,1516,1344,1375,1406,1688,1344,1297,1265
平均:1593.80毫秒。 (注意一个特别尴尬的结果)
Opera 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毫秒
反向做/同时:391,407,391,391,500,407,407,406,406,406
平均:411.20毫秒。
FireFox 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毫秒。
反向做/同时:414,381,389,383,388,389,381,387,400,379
平均:389.10毫秒。
我之前和document.getElementsByClassName()有一个非常类似的问题。 我当时不知道节点列表是什么。
var elements = document.getElementsByTagName('div'); for (var i=0; i<elements.length; i++) { doSomething(elements[i]); }
我的问题是,我期望的元素将是一个数组,但事实并非如此。 nodelist Document.getElementsByTagName()返回是可迭代的,但是你不能调用它的array.prototype方法。
但是,您可以像这样使用nodelist元素填充数组:
var myElements = []; for (var i=0; i<myNodeList.length; i++) { var element = myNodeList[i]; myElements.push(element); };
之后,您可以随意调用.innerHTML或.style或您的数组元素。
胡安·门德斯 ( Juan Mendez )提供的循环forms非常有用和实用,我稍微改了一下,所以现在它的工作原理也是 – 假,空,零和空string。
var items = [ true, false, null, 0, "" ]; for(var i = 0, item; (item = items[i]) !== undefined; i++) { console.log("Index: " + i + "; Value: " + item); }
你也可以在我的网站上查看这个页面,在那里我比较了一个递增循环,一个反向do/while
循环和Duff的设备的速度。
我知道你不想听到这个,但是:我认为在这种情况下最好的做法是最可读的。 只要循环不算从这里到月球,性能增益就不够高。
我更喜欢for循环,因为它更可读。 从长度循环到0会比从0循环到长度更有效率。 如你所说,使用反转while循环比foor循环更有效率。 我没有带有比较结果的页面的链接,但是我记得不同的浏览器有所不同。 对于某些浏览器来说,反转while循环快了一倍。 然而,如果你正在循环“小”的数组,那么它没有什么区别。 在你的例子中,元素的长度将是“小”
我想你有两个select。 对于像DOM这样的DOM元素,框架给了你一个很好的迭代方法。 第二种方法是for循环。
如果元素集是根节点的子元素,我喜欢使用TreeWalker 。