ChatGPT解决这个技术问题 Extra ChatGPT

为什么使用“for...in”进行数组迭代是个坏主意?

我被告知不要在 JavaScript 中对数组使用 for...in。为什么不?

我看到最近有人对你这么说的问题,但他们只针对数组。遍历数组被认为是不好的做法,但不一定要遍历对象的成员。
许多带有“for”循环的答案,例如 'for (var i=0; i
@MarkSchultheiss,但这是反向迭代。是否有另一个更快的前向迭代版本?
@Wynand 使用 var i = hCol1.length; for (i;i;i--;) {} 缓存 i 因为它会有所作为,并简化测试。 - 浏览器越老,forwhile 之间的区别就越大.并注意 var i = 1000; for (i; i; i--) {}var b =1000 for (b; b--;) {},其中 i 变为 1000 到 1,b 变为 999 到 0。- 浏览器越老,时间越倾向于性能。
你也可以很聪明。 for(var i = 0, l = myArray.length; i < l; ++i) ... 是通过前向迭代可以获得的最快和最好的。

K
Kenan Banks

原因是一个构造:

var a = []; // 创建一个新的空数组。一[5] = 5; // 调整数组大小的完全合法的 JavaScript。 for (var i = 0; i < a.length; i++) { // 正如大家所期望的那样,迭代从 0 到 5 的数字索引。控制台.log(a[i]); } /* 将显示: undefined undefined undefined undefined undefined 5 */

有时可能完全不同:

var a = [];一[5] = 5; for (var x in a) { // 仅显示显式设置的索引“5”,忽略 0-4 console.log(x); } /* 将显示:5 */

另请考虑 JavaScript 库可能会执行此类操作,这将影响您创建的任何数组:

// 在你的 JavaScript 库深处... Array.prototype.foo = 1; // 现在你不知道下面的代码会做什么。 var a = [1, 2, 3, 4, 5]; for (var x in a){ // 现在 foo 是每个数组的一部分, // 将在这里显示为 'x' 的值。控制台.log(x); } /* 将显示:0 1 2 3 4 foo */


请记住使用 (var x in a) 而不是 (x in a) - 不要创建全局。
第一个问题不是它不好的原因,只是语义上的差异。在我看来,第二个问题似乎是一个原因(除了库之间的冲突),改变内置数据类型的原型是不好的,而不是 for..in 是不好的。
@Stewart:JS 中的 所有对象 都是关联的。 JS 数组是一个对象,所以是的,它也是关联的,但这不是它的用途。如果要迭代对象的 ,请使用 for (var key in object)。但是,如果您想遍历数组的 元素,请使用 for(var i = 0; i < array.length; i += 1)
您在第一个示例中说过,它迭代从 0 到 4 的数字索引,正如每个人所期望的那样,我希望它从 0 迭代到 5!因为如果您在位置 5 添加一个元素,该数组将有 6 个元素(其中 5 个未定义)。
我认为如果将 a[5] 设置为 5 以外的值,例如 a[5]=42,这些示例会更加清晰。第二个示例 ("for (x in a)" 仅枚举一个值这一事实对我来说并不奇怪;它枚举的值是 5 而不是 42 这一事实让我感到惊讶(来自具有类似结构的其他语言枚举列表内容而不是数组索引)。
B
Bergi

for-in 语句本身并不是“坏习惯”,但它可能会被误用,例如,迭代 数组或类似数组的对象。

for-in 语句的目的是枚举对象属性。该语句将在原型链中上升,还会枚举 inherited 属性,这是 有时 不需要的。

此外,规范不保证迭代的顺序,这意味着如果你想“迭代”一个数组对象,使用这个语句你不能确定属性(数组索引)将按数字顺序访问。

例如,在 JScript (IE <= 8) 中,即使在 Array 对象上的枚举顺序也被定义为属性被创建:

var array = [];
array[2] = 'c';
array[1] = 'b';
array[0] = 'a';

for (var p in array) {
  //... p will be "2", "1" and "0" on IE
}

此外,谈到继承的属性,例如,如果您扩展 Array.prototype 对象(就像 MooTools 所做的一些库一样),也会枚举该属性:

Array.prototype.last = function () { return this[this.length-1]; };

for (var p in []) { // an empty array
  // last will be enumerated
}

正如我之前所说的对数组或类似数组的对象进行迭代,最好的办法是使用 顺序循环,例如普通的 for/{2 } 环形。

如果您只想枚举对象的自己的属性(那些未被继承的),您可以使用 hasOwnProperty 方法:

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

有些人甚至建议直接从 Object.prototype 调用该方法,以避免有人向我们的对象添加名为 hasOwnProperty 的属性时出现问题:

for (var prop in obj) {
  if (Object.prototype.hasOwnProperty.call(obj, prop)) {
    // prop is not inherited
  }
}

另请参阅 David Humphrey 的帖子 Iterating over Objects in JavaScript Quickly - 数组的 for..in 比“正常”循环慢得多。
关于“hasOwnProperty”最后一点的问题:如果有人在一个对象上覆盖了“hasOwnProperty”,你就会遇到问题。但是如果有人重写“Object.prototype.hasOwnProperty”,你会不会遇到同样的问题?不管怎样,他们把你搞砸了,这不是你的责任,对吧?
您说 for..in 是不错的做法,但它可能会被滥用。您是否有一个真实世界的良好实践示例,您真的想要检查所有对象属性,包括继承的属性?
@ScottRippey:如果你想去那里:youtube.com/watch?v=FrFUI591WhI
有了这个答案,我发现可以使用 for (var p in array) { array[p]; } 访问该值
C
Christoph

不应使用 for..in 迭代数组元素的三个原因:

for..in 将遍历不是 DontEnum 的数组对象的所有自己和继承的属性;这意味着如果有人向特定的数组对象添加属性(这是有正当理由的 - 我自己已经这样做了)或更改了 Array.prototype (这在代码中被认为是不好的做法,应该与其他脚本一起工作),这些属性也将被迭代;可以通过检查 hasOwnProperty() 来排除继承的属性,但这对您在数组对象本身中设置的属性没有帮助

for..in 不能保证保留元素顺序

它很慢,因为您必须遍历数组对象的所有属性及其整个原型链,并且仍然只会获取属性的名称,即要获取值,需要进行额外的查找


P
Praveen Kumar Purushothaman

因为 for...in 枚举的是保存数组的对象,而不是数组本身。如果我在数组原型链中添加一个函数,那也将包括在内。 IE

Array.prototype.myOwnFunction = function() { alert(this); }
a = new Array();
a[0] = 'foo';
a[1] = 'bar';
for(x in a){
 document.write(x + ' = ' + a[x]);
}

这将写:

0 = foo
1 = bar
myOwnFunction = function() { alert(this); }

而且由于您永远无法确定不会将任何内容添加到原型链中,因此只需使用 for 循环来枚举数组:

for(i=0,x=a.length;i<x;i++){
 document.write(i + ' = ' + a[i]);
}

这将写:

0 = foo
1 = bar

所以在这种情况下更好的说法是:“不要在数组上使用 for-in,因为它会包含你添加到原型中的任何东西”?
M
MarcG

从 2016 (ES6) 开始,我们可能会使用 for…of 进行数组迭代,正如 John Slegers 已经注意到的那样。

我只想添加这个简单的演示代码,以使事情更清楚:

Array.prototype.foo = 1;
var arr = [];
arr[5] = "xyz";

console.log("for...of:");
var count = 0;
for (var item of arr) {
    console.log(count + ":", item);
    count++;
    }

console.log("for...in:");
count = 0;
for (var item in arr) {
    console.log(count + ":", item);
    count++;
    }

控制台显示:

for...of:

0: undefined
1: undefined
2: undefined
3: undefined
4: undefined
5: xyz

for...in:

0: 5
1: foo

换句话说:

for...of 计数从 0 到 5,并且也忽略 Array.prototype.foo。它显示数组值。

for...in 仅列出 5,忽略未定义的数组索引,但添加 foo。它显示数组属性名称。


W
WynandB

简短的回答:这不值得。

更长的答案:即使不需要顺序元素顺序和最佳性能,这也不值得。

长答案:这不值得......

使用 for (var property in array) 将导致数组作为对象进行迭代,遍历对象原型链并最终执行比基于索引的 for 循环慢。

正如人们所期望的那样,for (... in ...) 不能保证按顺序返回对象属性。

使用 hasOwnProperty() 和 !isNaN() 检查来过滤对象属性是一个额外的开销,导致它执行得更慢并且否定了首先使用它的关键原因,即因为更简洁的格式。

由于这些原因,甚至不存在性能和便利性之间可接受的权衡。除非打算将数组作为对象处理并对数组的对象属性执行操作,否则实际上没有任何好处。


E
Eric King

单独来看,在数组上使用 for-in 并没有错。 For-in 迭代对象的属性名称,在“开箱即用”数组的情况下,属性对应于数组索引。 (迭代中不包含 lengthtoString 等内置属性。)

但是,如果您的代码(或您正在使用的框架)将自定义属性添加到数组或数组原型,那么这些属性将包含在迭代中,这可能不是您想要的。

一些 JS 框架,例如 Prototype 修改了 Array 原型。 JQuery 等其他框架没有,因此使用 JQuery,您可以安全地使用 for-in。

如果你有疑问,你可能不应该使用 for-in。

遍历数组的另一种方法是使用 for 循环:

for (var ix=0;ix<arr.length;ix++) alert(ix);

但是,这有一个不同的问题。问题是 JavaScript 数组可能有“洞”。如果您将 arr 定义为:

var arr = ["hello"];
arr[100] = "goodbye";

那么数组有两个项目,但长度为 101。使用 for-in 将产生两个索引,而 for 循环将产生 101 个索引,其中 99 的值为 undefined


c
ctmiddle

除了其他答案中给出的原因之外,如果您需要对计数器变量进行数学运算,您可能不想使用“for...in”结构,因为循环遍历对象属性的名称,因此变量是一个字符串。

例如,

for (var i=0; i<a.length; i++) {
    document.write(i + ', ' + typeof i + ', ' + i+1);
}

将会写

0, number, 1
1, number, 2
...

然而,

for (var ii in a) {
    document.write(i + ', ' + typeof i + ', ' + i+1);
}

将会写

0, string, 01
1, string, 11
...

当然,这可以很容易地通过包括

ii = parseInt(ii);

在循环中,但第一个结构更直接。


除非您确实需要整数或忽略无效字符,否则您可以使用前缀 + 而不是 parseInt
此外,不建议使用 parseInt()。尝试 parseInt("025");,它失败。
@Derek朕会功夫-您绝对可以使用parseInt。问题是如果您不包含基数,旧版浏览器可能会尝试解释该数字(因此 025 变为八进制)。这在 ECMAScript 5 中已修复,但对于以“0x”开头的数字仍然会发生(它将数字解释为十六进制)。为了安全起见,请使用基数来指定数字,如 parseInt("025", 10) - 指定基数为 10。
c
cHao

除了 for...in 循环遍历所有可枚举属性(这与“所有数组元素”相同!),请参阅 http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf,第 12.6.4 节(第 5 版)或 13.7.5.15(第 7 版):

枚举属性的机制和顺序......没有指定......

(强调我的。)

这意味着如果浏览器愿意,它可以按照插入的顺序遍历属性。或按数字顺序。或者按词法顺序(其中“30”在“4”之前!记住所有对象键——因此,所有数组索引——实际上都是字符串,所以这很有意义)。如果它将对象实现为哈希表,它可以按桶遍历它们。或者采取任何一个并添加“向后”。浏览器甚至可以随机迭代并符合 ECMA-262 标准,只要它只访问每个属性一次。

实际上,目前大多数浏览器都喜欢以大致相同的顺序进行迭代。但没有什么说他们必须这样做。这是特定于实现的,如果发现另一种方法更有效,则可能随时更改。

无论哪种方式,for...in 都没有顺序的含义。如果您关心顺序,请明确说明并使用带有索引的常规 for 循环。


L
Lior

主要有两个原因:

就像其他人所说的那样,您可能会得到不在数组中或从原型继承的键。因此,假设一个库向 Array 或 Object 原型添加了一个属性:

Array.prototype.someProperty = true

你会得到它作为每个数组的一部分:

for(var item in [1,2,3]){
  console.log(item) // will log 1,2,3 but also "someProperty"
}

你可以用 hasOwnProperty 方法解决这个问题:

var ary = [1,2,3];
for(var item in ary){
   if(ary.hasOwnProperty(item)){
      console.log(item) // will log only 1,2,3
   }
}

但这适用于使用 for-in 循环迭代任何对象。

通常数组中项的顺序很重要,但是for-in循环不一定会以正确的顺序迭代,那是因为它将数组视为对象,这是它在JS中实现的方式,而不是作为一个数组。这似乎是一件小事,但它确实会搞砸应用程序并且难以调试。


Object.keys(a).forEach( function(item) { console.log(item) } ) 迭代自有属性键的数组,而不是从原型继承的那些。
没错,但就像 for-in 循环一样,它不一定是正确的索引顺序。此外,它不适用于不支持 ES5 的旧浏览器。
您可以通过在脚本中插入特定代码来训练这些浏览器 array.forEach。请参阅 Polyfill developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
当然,但是您正在操纵原型,这并不总是一个好主意……而且,您仍然有订单问题……
J
John Slegers

我认为我没有太多要添加的内容。 Triptych's answerCMS's answer 关于为什么在某些情况下应避免使用 for...in

不过,我确实想补充一点,在现代浏览器中,在无法使用 for...in 的情况下,可以使用 for...in 的替代方法。该替代方案是 for...of

for (var item of items) {
    console.log(item);
}

笔记 :

不幸的是,没有任何版本的 Internet Explorer 支持 for...ofEdge 12+ 支持),因此您必须等待更长时间才能在客户端生产代码中使用它。但是,在您的服务器端 JS 代码中使用它应该是安全的(如果您使用 Node.js)。


v
vava

因为它通过对象字段而不是索引进行枚举。你可以用索引“长度”获得价值,我怀疑你想要这个。


在 Firefox 3 中,您也可以使用 arr.forEach 或 for(var [i, v] in Iterator(arr)) {} 但这些都不适用于 IE,尽管您可以自己编写 forEach 方法。
这个答案是错误的。 "lenght" 不会包含在 for-in 迭代中。仅包含您自己添加的属性。
M
Matthew Gardner

for ... in ... 的问题——只有当程序员并不真正理解该语言时才会出现问题;它不是真正的错误或任何东西——它迭代了一个对象的 所有 成员(嗯,所有 可枚举 成员,但现在这是一个细节)。当您想要迭代 just 数组的索引属性时,保持语义一致的唯一保证方法是使用整数索引(即 for (var i = 0; i < array.length; ++i) 样式循环)。

任何对象都可以具有与之关联的任意属性。特别是,将附加属性加载到数组实例上并没有什么可怕的。想要查看 only 索引的类数组属性的代码因此 必须 坚持使用整数索引。完全了解 for ... in 的功能并且真正需要查看所有属性的代码,那也可以。


很好的解释 尖尖的。只是好奇。如果我有一个位于乘法属性下的对象内部的数组并执行 for in,与常规 for 循环相比,这些数组会被迭代吗? (本质上会降低性能,对吗?)
@NiCkNewman 以及您在 for ... in 循环中 in 之后引用的对象将只是
我懂了。只是好奇,因为我的主要游戏对象中有对象和数组,并且想知道对于局来说这是否会比索引上的常规 for 循环更痛苦。
@NiCkNewman 整个问题的主题是您不应该在数组上使用 for ... in ;有很多很好的理由不这样做。这与其说是性能问题,不如说是“确保它不会破坏”问题。
好吧,从技术上讲,我的对象存储在一个数组中,这就是我担心的原因,例如:[{a:'hey',b:'hi'},{a:'hey',b:'hi'}],但是是的,我理解。
R
Redu

TL&DR: 在数组中使用 for in 循环并不是坏事,实际上恰恰相反。

如果在数组中正确使用,我认为 for in 循环是 JS 的瑰宝。您应该完全控制您的软件并知道自己在做什么。让我们看看提到的缺点并一一反驳它们。

它也循环继承的属性:首先,任何对 Array.prototype 的扩展都应该使用 Object.defineProperty() 完成,并且它们的可枚举描述符应该设置为 false。根本不应该使用任何不这样做的库。稍后添加到继承链中的属性会被计算在内:当通过 Object.setPrototypeOf 或通过 Class 扩展进行数组子类化时。您应该再次使用 Object.defineProperty(),它默认将可写、可枚举和可配置的属性描述符设置为 false。让我们在这里看一个数组子类化示例......

函数栈(...a){ var stack = new Array(...a); Object.setPrototypeOf(stack, Stack.prototype);返回栈; } Stack.prototype = Object.create(Array.prototype); // 现在堆栈可以完全访问数组方法。 Object.defineProperty(Stack.prototype,"constructor",{value:Stack}); // 现在 Stack 是一个正确的构造函数 Object.defineProperty(Stack.prototype,"peak",{value: function(){ // 将 Stack "only" 方法添加到 Stack.prototype. return this[this.length-1] ; } }); var s = new Stack(1,2,3,4,1);控制台.log(s.peak()); s[s.length] = 7; console.log("长度:",s.length); s.push(42); console.log(JSON.stringify(s)); console.log("长度:",s.length); for(var i in s) console.log(s[i]);

所以你看.. for in 循环现在是安全的,因为你关心你的代码。

for in 循环很慢:不。如果您正在循环遍历不时需要的稀疏数组,这是迄今为止最快的迭代方法。这是人们应该知道的最重要的性能技巧之一。让我们看一个例子。我们将遍历一个稀疏数组。

var a = []; a[0] = "零"; a[10000000] = "一千万"; console.time("for 循环数组 a:"); for(var i=0; i < a.length; i++) a[i] && console.log(a[i]); console.timeEnd("for 循环数组 a:"); console.time("for in 循环数组 a:"); for(var i in a) a[i] && console.log(a[i]); console.timeEnd("for in 循环数组 a:");


m
matpop

此外,由于语义,for, in 处理数组的方式(即与任何其他 JavaScript 对象相同)与其他流行语言不一致。

// C#
char[] a = new char[] {'A', 'B', 'C'};
foreach (char x in a) System.Console.Write(x); //Output: "ABC"

// Java
char[] a = {'A', 'B', 'C'};
for (char x : a) System.out.print(x);          //Output: "ABC"

// PHP
$a = array('A', 'B', 'C');
foreach ($a as $x) echo $x;                    //Output: "ABC"

// JavaScript
var a = ['A', 'B', 'C'];
for (var x in a) document.write(x);            //Output: "012"

d
dc1

除了其他问题之外,“for..in”语法可能更慢,因为索引是字符串,而不是整数。

var a = ["a"]
for (var i in a)
    alert(typeof i)  // 'string'
for (var i = 0; i < a.length; i++)
    alert(typeof i)  // 'number'

可能没有太大关系。数组元素是基于数组或类数组对象的属性,所有对象属性都有字符串键。除非您的 JS 引擎以某种方式对其进行了优化,否则即使您使用了一个数字,它最终也会变成一个字符串以供查找。
不管有什么性能问题,如果您是 JavaScript 新手,请使用 var i in a 并期望索引是整数,那么执行 a[i+offset] = <value> 之类的操作会将值放在完全错误的位置。 ("1" + 1 == "11")。
P
Pierz

一个重要的方面是 for...in 仅迭代对象中包含的属性,这些属性的 enumerable property 属性 设置为 true。因此,如果尝试使用 for...in 迭代对象,那么如果它们的可枚举属性属性为 false,则可能会丢失任意属性。很可能更改普通 Array 对象的可枚举属性属性,以便不枚举某些元素。尽管通常属性属性倾向于应用于对象内的函数属性。

可以通过以下方式检查属性的可枚举属性的值:

myobject.propertyIsEnumerable('myproperty')

或者获取所有四个属性属性:

Object.getOwnPropertyDescriptor(myobject,'myproperty')

这是 ECMAScript 5 中可用的功能 - 在早期版本中,无法更改可枚举属性的值(它始终设置为 true)。


P
Peter Mortensen

for/in 适用于两种类型的变量:哈希表(关联数组)和数组(非关联)。

JavaScript 将自动确定其通过项目的方式。因此,如果您知道您的数组确实是非关联的,您可以使用 for (var i=0; i<=arrayLen; i++),并跳过自动检测迭代。

但在我看来,最好使用 for/in,自动检测所需的过程非常小。

对此的真正答案将取决于浏览器解析器/解释 JavaScript 代码的方式。它可以在浏览器之间更改。

我想不出不使用 for/in 的其他目的;

//Non-associative
var arr = ['a', 'b', 'c'];
for (var i in arr)
   alert(arr[i]);

//Associative
var arr = {
   item1 : 'a',
   item2 : 'b',
   item3 : 'c'
};

for (var i in arr)
   alert(arr[i]);

for ... in 适用于对象。没有自动检测之类的东西。
W
Willem van der Veen

以下是为什么这是(通常)不好的做法的原因:

for...in 循环遍历它们自己的所有可枚举属性及其原型的可枚举属性。通常在数组迭代中,我们只想迭代数组本身。即使您自己可能不会向数组中添加任何内容,您的库或框架也可能会添加一些内容。

例子:

Array.prototype.hithere = '这里'; var 数组 = [1, 2, 3]; for (let el in array){ // hithere 属性也将在 console.log(el) 上进行迭代; }

for...in 循环不保证特定的迭代顺序。尽管如今在大多数现代浏览器中通常可以看到顺序,但仍然没有 100% 的保证。 for...in 循环忽略未定义的数组元素,即尚未分配的数组元素。

例子::

常量 arr = []; arr[3] = 'foo'; // 将数组大小调整为 4 arr[4] = undefined; // 添加另一个值为 undefined 的元素 // 遍历数组,for 循环确实显示未定义的元素 for (let i = 0; i < arr.length; i++) { console.log(arr[i]) ; } console.log('\n'); // for in 会忽略未定义的元素 for (let el in arr) { console.log(arr[el]); }


J
JAL

因为如果您不小心,它将遍历原型链上属于对象的属性。

您可以使用 for.. in,但请务必使用 hasOwnProperty 检查每个属性。


还不够 - 将任意命名属性添加到数组实例是完全可以的,这些属性将从 hasOwnProperty() 检查中测试 true
好点,谢谢。我从来没有傻到自己对 Array 这样做,所以我没有考虑过!
@Pointy我没有测试过这个,但也许可以通过对每个属性名称使用 isNaN 检查来克服这个问题。
@Wynand 有趣的想法;但是,我真的不明白为什么在使用简单的数字索引进行迭代如此容易时值得麻烦。
@WynandB 抱歉,我觉得有必要进行更正:isNaN 用于检查变量是否为特殊值 NaN,它不能用于检查“数字以外的事物”(你可以去使用常规类型)。
j
josh3736

这不是一定不好(基于您正在做的事情),但对于数组,如果已将某些内容添加到 Array.prototype,那么您将得到奇怪的结果。您希望此循环运行 3 次的地方:

var arr = ['a','b','c'];
for (var key in arr) { ... }

如果一个名为 helpfulUtilityMethod 的函数已添加到 Arrayprototype,那么您的循环将最终运行四次:key 将是 012 和 {1 }。如果你只期待整数,哎呀。


u
user268396

您应该只在属性列表上使用 for(var x in y),而不是在对象上(如上所述)。


只是关于 SO 的注释 - 没有“上方”,因为评论一直在更改页面上的顺序。所以,我们真的不知道你的意思是什么评论。出于这个原因,最好说“在 x 人的评论中”。
c
chumakoff

对数组使用 for...in 循环并没有错,尽管我可以猜到为什么有人告诉你:

1.) 已经有一个用于数组的高阶函数或方法,但具有更多功能和更精简的语法,称为“forEach”:Array.prototype.forEach(function(element, index, array) {} );

2.) 数组总是有一个长度,但 for...inforEach 不对任何值为 'undefined' 的值执行函数,只对定义了值的索引执行。因此,如果您只分配一个值,这些循环只会执行一次函数,但由于枚举了一个数组,它的长度总是会达到具有定义值的最高索引,但在使用这些时该长度可能会被忽视循环。

3.) 标准 for 循环将按照您在参数中定义的次数执行函数,并且由于数组是编号的,因此定义要执行函数的次数更有意义。与其他循环不同,for 循环可以为数组中的每个索引执行一个函数,无论该值是否已定义。

本质上,您可以使用任何循环,但您应该准确记住它们的工作方式。了解不同循环重复的条件、它们各自的功能,并意识到它们或多或少适用于不同的场景。

此外,通常使用 forEach 方法比使用 for...in 循环可能被认为是更好的做法,因为它更容易编写并且具有更多功能,因此您可能希望养成只使用此方法的习惯和标准,但你的电话。

见下文,前两个循环只执行一次 console.log 语句,而标准 for 循环按指定的次数执行该函数,在这种情况下,array.length = 6。

var arr = [];
arr[5] = 'F';

for (var index in arr) {
console.log(index);
console.log(arr[index]);
console.log(arr)
}
// 5
// 'F'
// => (6) [undefined x 5, 6]

arr.forEach(function(element, index, arr) {
console.log(index);
console.log(element);
console.log(arr);
});
// 5
// 'F'
// => Array (6) [undefined x 5, 6]

for (var index = 0; index < arr.length; index++) {
console.log(index);
console.log(arr[index]);
console.log(arr);
};
// 0
// undefined
// => Array (6) [undefined x 5, 6]

// 1
// undefined
// => Array (6) [undefined x 5, 6]

// 2
// undefined
// => Array (6) [undefined x 5, 6]

// 3
// undefined
// => Array (6) [undefined x 5, 6]

// 4
// undefined
// => Array (6) [undefined x 5, 6]

// 5
// 'F'
// => Array (6) [undefined x 5, 6]

M
Maher Tliba

for...in 循环总是枚举键。对象属性键始终是字符串,即使是数组的索引属性:

var myArray = ['a', 'b', 'c', 'd'];
var total = 0
for (elem in myArray) {
  total += elem
}
console.log(total); // 00123

A
Alireza

for...in 在 JavaScript 中处理对象时很有用,但不适用于数组,但我们仍然不能说这是错误的方式,但不推荐使用 for...in 循环查看下面的示例:

let txt = "";
const person = {fname:"Alireza", lname:"Dezfoolian", age:35}; 
for (const x in person) {
    txt += person[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 

好的,现在让我们用 Array 来做:

let txt = "";
const person = ["Alireza", "Dezfoolian", 35]; 
for (const x in person) {
   txt += person[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 

如您所见,结果相同...

但是让我们尝试一些东西,让我们为 Array 制作一些原型......

Array.prototype.someoneelse = "someoneelse";

现在我们创建一个新的 Array();

let txt = "";
const arr = new Array();
arr[0] = 'Alireza';
arr[1] = 'Dezfoolian';
arr[2] = 35;
for(x in arr) {
 txt += arr[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 someoneelse

你看到了其他人!......在这种情况下,我们实际上循环了新的 Array 对象!

所以这就是我们需要谨慎使用 for..in 的原因之一,但并非总是如此......


B
Badr Elmers

由于 JavaScript 元素被保存为标准对象属性,因此不建议使用 for...in 循环遍历 JavaScript 数组,因为将列出普通元素和所有可枚举属性。

来自https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Indexed_collections


j
jcomeau_ictx

虽然这个问题没有具体解决,但我要补充一点,有一个很好的理由永远不要在 NodeList 中使用 for...in (就像从 querySelectorAll 调用中获得的那样,因为它看不到完全返回元素,而不是仅遍历 NodeList 属性。

在单一结果的情况下,我得到:

var nodes = document.querySelectorAll(selector);
nodes
▶ NodeList [a._19eb]
for (node in nodes) {console.log(node)};
VM505:1 0
VM505:1 length
VM505:1 item
VM505:1 entries
VM505:1 forEach
VM505:1 keys
VM505:1 values

这解释了为什么我的 for (node in nodes) node.href = newLink; 失败了。