如果我在for语句中使用object.getElementsByTagName(tagName)

时间:2013-08-12 07:44:40

标签: javascript dom nodelist

如果我在for语句中使用object.getElementsByTagName(tagName)

for (index = 0; index < object.getElementsByTagName(tagName).length; index++) {
object.getElementsByTagName(tagName)[index].property = value;
}

浏览器是否为每次循环实例化实例化一个新的nodeList对象,或者浏览器是否每次只引用一个生成的列表;或者,它可以实例化一个列表,引用指定的对象并在每次通过循环时卸载列表对象?

我一直想知道将nodeList对象存储到变量是否更好,并在需要时引用它。

5 个答案:

答案 0 :(得分:1)

  

浏览器是否为每次循环实例化实例化一个新的nodeList对象,或者浏览器是否每次只引用一个生成的列表;

您可以通过测试对getElementsByTagName的两次调用是否返回同一对象来轻松测试:

document.getElementsByTagName('div') === document.getElementsByTagName('div')
// true

似乎使用相同的参数调用此方法确实会返回相同的NodeList对象(至少在Firefox和Chrome中)。 每次都不会生成新列表

所以你可能会认为一遍又一遍地在循环中调用它不会有所作为。但是,正如我所看到的,有多种原因可以将列表存储在变量中:

  • 每个循环都有一个不必要的函数调用。

  • 你不知道幕后究竟发生了什么。即使返回相同的对象,也可能是正在运行的过程,这些过程确保列表反映了文档的当前状态,如果您没有调用该函数,则不会发生这种情况。

  • 最重要的是:The DOM specification似乎并不要求返回相同的列表。它在Firefox和Chrome(我测试过它)中可能只是一个实现细节,所以我不会依赖这种行为。实际上,规范明确规定返回 new 列表:

      

    返回值:包含所有匹配元素的新NodeList对象。


  

我一直想知道将nodeList对象存储到变量是否更好,并在需要时引用它。

是的。添加或删除元素时甚至不必再次调用getElementsByTagName,因为返回的NodeList 实时,即对文档所做的任何更改都会直接反映在列表,没有你明确更新 某些操作(如访问列表的length)也会触发重新评估。因此,除了将列表存储在变量中之外,您可能还希望缓存长度:

var nodes = object.getElementsByTagName(tagName);

for (var index = 0, l = nodes.length; index < l; index++) {
    nodes[index].property = value;
}

根据您的需要,这可能非常方便或非常混乱。如果您只想要调用函数时存在的节点列表,则必须将NodeList转换为数组:

var nodes = Array.prototype.slice.call(object.getElementsByTagName(tagName), 0);

答案 1 :(得分:1)

这是更好的代码,原因如下:

var elems = object.getElementsByTagName(tagName);
for (var index = 0, len = elems.length; index < len; index++) {
    elems[index].property = value;
}
  1. 不能多次拨打getElementsByTagName()。它只获取一次nodeList。
  2. 为了提高效率,它不依赖于任何特定的浏览器实现。
  3. 它会预加载nodeList的长度,所以即使每次循环都没有重新获取。
  4. 这是让代码更有效的更安全的方式。
  5. 更安全的代码会使您提出的实施问题无关紧要。

答案 2 :(得分:0)

您的浏览器每次都会生成一个新列表。 但是这段代码将在index改变每次运行时起作用。

首先在某些var中保存节点列表是个好习惯:

var elements = object.getElementsByTagName(tagName);

for (index = 0; index < elements; index++) {
        elements[index].property = value;
}

你可以看到你的aproach:

var index = 0;

while(index<object.getElementsByTagName(tagName))
{
  elements[index].property = value;
  index++;
}

答案 3 :(得分:0)

这个测试显示它首先得到数组每次计算长度!

var testObj = {
    getArray: function () {
        console.log( 'getting array' );
        return this._array;
    },
    _array: ['a','b','c']
};

for ( var index = 0; index < testObj.getArray().length; index++ ){
    document.write( testObj.getArray()[index] );
}

我要先存储元素,每次都引用它的.length,或者只是将长度存储在变量中:)

答案 4 :(得分:0)

您期望它做什么?读你的想法?您是否期望JS处理器具有object.getElementsByTagName(tagName)每次调用时(可能)返回相同值的语义信息,因此是“循环不变”,因此可以从循环中提升(参见循环) http://en.wikipedia.org/wiki/Loop-invariant_code_motion)?

也许你想象浏览器以某种方式“缓存”元素下具有特定标记名的所有元素的列表,然后在后续调用中返回相同的列表?虽然很难在没有查看代码的情况下判断浏览器在做什么,但可能不是,或者一个浏览器可能是另一个浏览器而不是。即使浏览器确实缓存了结果,也认为在整个方案中任何性能损失可能都很小,一次又一次地调用API将总是比引用已经设置的变量的效率低得多。对节点列表的每个元素执行的操作(例如设置属性)可能会导致重置缓存,这并非不可能。

作为一个小问题,假设列表是一遍又一遍地创建的,那么它就不会被“卸载”; JS中没有这样的概念。从技术角度来说,它的地位将“只是坐在那里”。很快就会收集垃圾。

所以是的,一定要只调用getElementsByTagName一次。实际上,你可以在你的程序开始时调用它一次(假设你只有一个你感兴趣的对象和标记名),因为它返回一个“实时”列表。请参阅https://developer.mozilla.org/en-US/docs/Web/API/element.getElementsByTagName