JavaScript 循环:for...in 与 for

IT技术 javascript arrays for-loop for-in-loop
2021-02-03 10:30:10

我在 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里面的value是for...in什么?

4个回答

不要for..in用于数组迭代。

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

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

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

Javascriptfor..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)
    }
}

更多关于原型继承

for..in在 Array 类型上使用结构的问题在于,对于属性的生成顺序没有保证……一般来说,这是处理数组的一个非常重要的特征。

另一个问题是它通常标准for实现

底线

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

for..in 结构是它继续搜索原型链上的可枚举属性。它还将迭代继承的可枚举属性......我对这部分有点模糊,你能解释一下吗
2021-03-19 10:30:10
这是正确的,但还有另一个原因要避免使用for ... infor 数组。从 MDC...for...in 语句将返回您的用户定义属性的名称以及数字索引。...
2021-03-22 10:30:10
“速度测试”存在致命缺陷。Object 没有 length 属性,所以 for 循环从 0 到undefined(即它甚至不进入循环)。
2021-04-09 10:30:10

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

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

如果在迭代buttonsfor...in具有:

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

您将看到它输出如下内容:

1
2
...
length
item

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

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

NodeLists 是实时的,这意味着每当您访问 eg 时length,列表都会更新(再次搜索元素)。因此,您应该避免对length.

例子:

for(var i = 0, l = buttons.length; i < l, i++)
@gilly3:实际上我更喜欢,for(var i = buttons.length; i--; )但有时顺序很重要。而且我可以想象这i in buttons仍然会重新评估列表(此外,它可能比数字比较慢)。
2021-03-23 10:30:10
为了避免调用长度,我更喜欢for (var i = 0; i in buttons; i++). 我喜欢这种检查的明确性,而不是隐式检查长度。
2021-03-26 10:30:10

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

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

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

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

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

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

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

遍历一个使用for..in会比使用for循环,因为它不仅可以访问两个属性,而一个for循环将尝试1001快得多。

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

a.forEach();

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