【问题标题】:JavaScript Loops: for...in vs forJavaScript 循环:for...in 与 for
【发布时间】:2011-07-12 22:33:48
【问题描述】:

我在 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');

for...in 中的button 的值是多少?

【问题讨论】:

  • 试试看:for ( var button in buttons ) alert( button ); 这样你就可以看到 for .. inbutton 设置为什么。

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


【解决方案1】:

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

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

  1. 属性不一定按顺序返回(即不是 0、1、2 等)
  2. 返回所有可枚举属性,包括非索引属性和[[Prototype]] 链上的属性。这会导致性能下降,因为可能需要进行 hasOwnProperty 测试以避免继承属性。

在 ES5 之前使用 for..in 的一个原因是为了提高稀疏数组的性能,前提是顺序无关紧要。例如,在下面:

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

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

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

a.forEach();

也只会按顺序迭代两个属性。

【讨论】:

    【解决方案2】:

    不要使用for..in 进行数组迭代。

    重要的是要了解用于访问索引的 Javascript 数组的方括号语法 ([]) 实际上是从 Object 继承的...

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

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

    Javascript 的for..in 旨在迭代对象 的属性。生成每个属性的key。使用这个 key 结合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
    }
    

    而且由于数组只是一个具有连续数字属性名称(索引)的对象,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 结构的问题在于,对于生成属性的顺序没有任何疑问......一般来说,这是处理数组时非常重要的特性。

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

    底线

    使用for...in 来迭代数组就像使用螺丝刀的枪托来钉钉子...为什么不直接使用锤子 (for)?

    【讨论】:

    • 这是正确的,但还有另一个原因是避免将for ... in 用于数组。 From MDC: ...for...in 语句将返回除数字索引之外的用户定义属性的名称。 ...
    • for..in 结构是它继续搜索原型链上的可枚举属性。它还将迭代继承的可枚举属性....我对这部分有点模糊,你能解释一下吗
    • “速度测试”存在致命缺陷。 Object 没有长度属性,因此 for 循环从 0 变为 undefined(即它甚至不进入循环)。
    【解决方案3】:

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

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

    如果用for...in 迭代buttons 和:

    for(var i in a) {
        console.log(i)
    }
    

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

    1
    2
    ...
    length
    item
    

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

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

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

    例子:

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

    【讨论】:

    • 为了避免调用长度,我更喜欢for (var i = 0; i in buttons; i++)。我喜欢这种检查的明确性,而不是隐式检查长度。
    • @gilly3:其实我更喜欢for(var i = buttons.length; i--; ),但有时顺序很重要。我可以想象i in buttons 仍然会重新评估列表(除了它可能比数字比较慢)。
    【解决方案4】:

    for(var key in obj) { } 迭代对象中的所有元素,包括其原型的元素。 因此,如果您正在使用它并且不知道扩展 Object.prototype 的任何内容,则应始终测试 obj.hasOwnProperty(key) 并在此检查返回 false 时跳过密钥。

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

    【讨论】:

      猜你喜欢
      • 2012-11-18
      • 1970-01-01
      • 2015-02-25
      • 1970-01-01
      • 2016-03-30
      • 2010-09-19
      • 2018-11-14
      • 2011-10-21
      相关资源
      最近更新 更多