JavaScript循环:for ... in vs for

时间:2011-03-10 18:08:25

标签: javascript arrays for-loop for-in-loop

我在Javascript中遇到了一个奇怪的行为。我得到了

“对象不支持此属性或方法”
以下代码中的removeAttribute函数的

异常:

var buttons = controlDiv.getElementsByTagName("button");
for ( var button in buttons )
    button.removeAttribute('disabled');

当我使用以下内容更改代码时,问题就会消失:

var buttons = controlDiv.getElementsByTagName("button");
for ( var i = 0; i < buttons.length; i++ )
    buttons[i].removeAttribute('disabled');

button内的for...in的价值是多少?

4 个答案:

答案 0 :(得分:44)

不要将for..in用于Array迭代。

重要的是要理解用于访问指标的Javascript Array的方括号语法([])实际上是从Object继承...

obj.prop === obj['prop']  // true

for..in结构不像其他语言(php,python等)中更传统的for..each/in那样工作。

Javascript的for..in旨在迭代对象的属性。生成每个属性的。使用此结合Object的括号语法,您可以轻松访问所追求的值。

var obj = {
    foo: "bar",
    fizz: "buzz",
    moo: "muck"
};

for ( var prop in obj ) {
    console.log(prop);      // foo / fizz / moo
    console.log(obj[prop]); // bar / buzz / muck
}

因为Array只是一个带有顺序数字属性名称(索引)的对象for..in以类似的方式工作,产生数字指示就像它产生上面的属性名一样

for..in结构的一个重要特征是它继续在原型链中搜索可枚举的属性。它还将迭代继承的可枚举属性。您可以验证当前属性是否直接存在于本地对象上,而不是hasOwnProperty()附加到的原型...

for ( var prop in obj ) {
    if ( obj.hasOwnProperty(prop) ) {
        // prop is actually obj's property (not inherited)
    }
}

More on Prototypal Inheritance

在Array类型上使用for..in结构的问题在于,没有关于属性生成顺序的重要信息......一般来说,这是处理数组的一个非常重要的特性。 / p>

另一个问题是它通常是slower than标准for实施。

底线

使用for...in迭代数组就像使用螺丝刀的屁股来钉钉子......为什么你不能只使用锤子(for)?

答案 1 :(得分:7)

当您想循环对象的属性时,将使用

for...in。但它与普通的for循环相同:循环变量包含当前的“索引”,表示对象的属性而不是值。

要迭代数组,您应该使用普通的for循环。 buttons不是数组,而是NodeList(类似数组的结构)。

如果buttonsfor...in一起使用:

进行迭代
for(var i in a) {
    console.log(i)
}

你会看到它输出的内容如下:

1
2
...
length
item

因为lengthitemNodeList类型对象的两个属性。因此,如果您天真地使用for..in,您将尝试访问buttons['length'].removeAttribute(),这将导致错误,因为buttons['length']是一个函数而不是DOM元素。

所以正确的方法是使用普通的for循环。但还有另一个问题:

NodeList是实时的,这意味着只要您访问,例如length,列表已更新(再次搜索元素)。因此,您应该避免对length进行不必要的调用。

示例:

for(var i = 0, l = buttons.length; i < l, i++)

答案 2 :(得分:0)

for(var key in obj) { }遍历对象中的所有元素,包括其原型的元素。 因此,如果您正在使用它并且无法知道扩展Object.prototype,则应始终测试obj.hasOwnProperty(key)并在此检查返回false时跳过该键。

for(start; continuation; loop)是一个C风格的循环:start在循环之前执行,continuation被测试并且循环仅在它为真时继续,loop在执行之后执行每一个循环。

答案 3 :(得分:0)

虽然for..in通常不应该用于数组,但是在ES5之前,有一种情况可以将它与稀疏数组一起使用。

正如其他答案所述,for..in和Arrays的主要问题是:

  1. 不一定按顺序返回属性(即不是0,1,2等)
  2. 返回所有可枚举属性,包括非索引属性和[[Prototype]]链上的属性。这会导致性能降低,因为可能需要 hasOwnProperty 测试以避免继承属性。
  3. 在ES5之前使用for..in的一个原因是为了提高稀疏数组的性能,只要顺序无关紧要。例如,在以下内容中:

    var a = [0];
    a[1000] = 1;
    

    使用for..in迭代 a 将比使用for循环快得多,因为它只会访问两个属性,而for循环将尝试1001。

    但是,ES5的 forEach 使这种情况变得多余,只能访问存在的成员,所以:

    a.forEach();
    

    也将按顺序迭代两个属性。