为什么在数组迭代中使用“ for…in”是个坏主意?


1823

有人告诉我不要for...in在JavaScript中使用数组。为什么不?


45
我看到了一个最近的问题,有人在对你说这句话,但它们只适用于数组。遍历数组被认为是不好的做法,但不一定遍历对象的成员。
mmurch 2010年

19
与“ for”循环有关的答案很多,例如“ for(var i = 0; i <hColl.length; i ++){}”与“ var i = hColl.length; 而(i--){}',则可以使用后一种形式时,速度要快得多。我知道这是切线的,但我想我会补充一点。
Mark Schultheiss

2
@MarkSchultheiss,但这是反向迭代。是否有另一个版本的正向迭代更快?
ma11hew28 '10 -10-10

5
@Wynand使用var i = hCol1.length; for (i;i;i--;) {}缓存i会有所作为,并简化测试。-浏览器越旧,与之间的差异就越大,for并且while总是缓存“ i”计数器-当然,负数并不总是适合这种情况,而负数obfuscate 对于某些人来说却有些许。并注意var i = 1000; for (i; i; i--) {}var b =1000 for (b; b--;) {}在那里我去1000:1和B去999 0 -旧的浏览器越多,而往往有利于性能。
Mark Schultheiss 2013年

9
您也可以变得聪明。for(var i = 0, l = myArray.length; i < l; ++i) ...正向迭代是最快,最好的。
Mathieu Amiot

Answers:


1557

原因是一种构造:

var a = []; // Create a new empty array.
a[5] = 5;   // Perfectly legal JavaScript that resizes the array.

for (var i = 0; i < a.length; i++) {
    // Iterate over numeric indexes from 0 to 5, as everyone expects.
    console.log(a[i]);
}

/* Will display:
   undefined
   undefined
   undefined
   undefined
   undefined
   5
*/

有时可能与另一个完全不同:

var a = [];
a[5] = 5;
for (var x in a) {
    // Shows only the explicitly set index of "5", and ignores 0-4
    console.log(x);
}

/* Will display:
   5
*/

还请考虑JavaScript库可能会执行以下操作,这会影响您创建的任何数组:

// Somewhere deep in your JavaScript library...
Array.prototype.foo = 1;

// Now you have no idea what the below code will do.
var a = [1, 2, 3, 4, 5];
for (var x in a){
    // Now foo is a part of EVERY array and 
    // will show up here as a value of 'x'.
    console.log(x);
}

/* Will display:
   0
   1
   2
   3
   4
   foo
*/


146
从历史上看,有些浏览器甚至在“ length”,“ toString”等上进行迭代!
bobince

398
记住要使用(var x in a)而不是(x in a)-不想创建全局。
克里斯·摩根

78
第一个问题不是不好的原因,只是语义上的差异。在我看来,第二个问题似乎是一个原因(在相同的库之间存在冲突),改变内置数据类型的原型是不好的,而不是for..in的不好。
斯图尔特

86
@Stewart:JS中的所有对象都是关联的。JS数组是一个对象,所以是的,它也是关联的,但这不是它的用途。如果要遍历对象的,请使用for (var key in object)。但是,如果要遍历数组的元素,请使用for(var i = 0; i < array.length; i += 1)
Martijn

42
您在第一个示例中说过,它像大家期望的那样在0到4的数字索引之间进行迭代,我希望它从0到5进行迭代!由于如果在位置5添加元素,则数组将具有6个元素(其中5个未定义)。
stivlo 2011年

393

for-in语句本身并不是一个“坏习惯”,但是它可能会被滥用,例如,在数组或类似数组的对象上进行迭代

for-in语句的目的是枚举对象属性。该语句将出现在原型链中,还会枚举继承的属性,这有时是不希望的。

同样,规范不能保证迭代的顺序。这意味着,如果您要“迭代”数组对象,则使用此语句无法确定将以数字顺序访问属性(数组索引)。

例如,在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/ while循环。

当您只想枚举对象自身的属性(未继承的属性)时,可以使用以下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
  }
}

10
另请参阅David Humphrey的“ 快速迭代JavaScript中的对象”一文 -因为数组的for..in循环比“常规”循环慢得多。
克里斯·摩根

17
关于关于“ hasOwnProperty”的最后一点的问题:如果有人在对象上覆盖“ hasOwnProperty”,您将遇到问题。但是,如果有人重写“ Object.prototype.hasOwnProperty”,您是否会遇到同样的问题?不管是哪种方式,它们都在使您烦恼,这不是您的责任吗?
Scott Rippey

您说for..in这不是一个坏习惯,但是可能会被滥用。您是否有一个良好实践的真实示例,您真的想遍历所有对象的属性,包括继承的属性吗?
rjmunro

4
@ScottRippey:如果您想去那里:youtube.com/watch?
Nathan Wall,

有了这个答案,我发现可以用for (var p in array) { array[p]; }
equiman

117

为什么不应该使用for..in遍历数组元素的三个原因:

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

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

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


55

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

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 =富
1 =条
myOwnFunction = function(){alert(this); }

并且由于您永远无法确定什么都不会添加到原型链中,因此只需使用for循环来枚举数组:

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

它将写为:

0 =富
1 =条

16
数组对象,没有“持有数组的对象”。
RobG

41

孤立地,在数组上使用for-in并没有错。for-in遍历对象的属性名称,对于“开箱即用”的数组,属性对应于数组索引。(内置样propertes 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-loop将产生101个索引,其中99的值为undefined


37

自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。它显示了数组属性名称


32

简短的回答:这是不值得的。


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


长答案:这不值得...

  • 使用for (var property in array)将导致array被迭代为一个对象,遍历对象原型链并最终比基于索引的for循环执行得慢。
  • for (... in ...) 不能保证可以按预期顺序返回对象属性。
  • 使用hasOwnProperty()!isNaN()检查来过滤对象属性是一个额外的开销,导致它执行起来甚至更慢,并且抵消了最初使用它的关键原因,即由于格式更加简洁。

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


31

除了其他答案中给出的原因外,如果您需要对计数器变量进行数学运算,则可能不希望使用“ 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);

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


6
您可以使用前缀+代替,parseInt除非您确实需要整数或忽略无效字符。
Konrad Borowski

另外,parseInt()不建议使用。尝试parseInt("025");,它就会失败。
德里克·朕会功夫2012年

6
@Derek朕会功夫-您可以使用parseInt。问题是如果您不包括基数,则较旧的浏览器可能会尝试解释数字(因此025变为八进制)。此问题已在ECMAScript 5中修复,但对于以“ 0x”开头的数字仍然会发生(它将数字解释为十六进制)。为了安全起见,可以使用基数来指定像这样的数parseInt("025", 10)-指定基座10
IAmTimCorey

23

除了一个事实,即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带有索引的常规循环。


18

主要有两个原因:

就像其他人所说的那样,您可能会得到不在数组中或从原型继承的键。所以,如果说,一个库将一个属性添加到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中实现的方式,而不是作为数组。这似乎是一件小事,但是它确实会使应用程序搞砸,并且很难调试。


2
Object.keys(a).forEach( function(item) { console.log(item) } )遍历一组自己的属性键,而不是从原型继承的属性键。
Qwerty 2014年

2
的确如此,但就像for-in循环一样,它不一定处于正确的索引顺序。另外,它不适用于不支持ES5的旧版浏览器。
Lior 2014年

您可以array.forEach通过在脚本中插入某些代码来教那些浏览器。见填充工具developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
Qwerty全

当然,但是然后您要操纵原型,这并不总是一个好主意……而且,仍然存在订单问题……
Lior

当然,原因之三:稀疏数组。
更好的Oliver

16

因为它是通过对象字段而不是索引枚举的。您可以通过索引“ length”获得价值,我怀疑您是否想要这样做。


那么什么是最好的方式呢?
lYriCAlsSH,2009年

3
for(var i = 0; i <arr.length; i ++){}
vava

3
在firefox 3中,您也可以使用arr.forEach或for(Iterator(arr)){}中的for(var [i,v]),但在IE中都不起作用,尽管您可以自己编写forEach方法。
vava

几乎每个库都有它自己的方法。
vava

5
这个答案是错误的。“长度”将不包含在for-in迭代中。只有您自己添加的属性才包括在内。
2009年

16

我认为我没有什么要补充的。Triptych的答案CMS的答案为何使用for...in在某些情况下应避免。

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

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

注意 :

不幸的是,没有任何版本的Internet Explorer支持for...ofEdge 12+支持),因此您必须稍等片刻,直到可以在客户端生产代码中使用它。但是,在服务器端JS代码中使用它应该是安全的(如果使用Node.js)。


@georgeawg您的意思for-of不是for-in,对吗?
ᆼ ᆺ ᆼ

15

问题是,for ... in ...当程序员不真正理解该语言时,这才成为问题。它并不是真正的bug或其他任何东西–而是遍历对象的所有成员(嗯,所有可枚举的成员,但这是现在的细节)。当你想迭代只是一个数组中,只保证的方式,让事情语义一致的索引属性是使用整数索引(即for (var i = 0; i < array.length; ++i)风格的循环)。

任何对象都可以具有与之关联的任意属性。特别是将额外的属性加载到数组实例上并没有什么可怕的。想要看到的代码阵列状的索引属性,因此必须坚持一个整数索引。完全知道做什么for ... in和真正需要查看所有属性的代码,那么那也没关系。


很好的解释尖尖。只是好奇。如果我有一个数组处于对象内的乘法属性下for in,并且与常规的for循环相比做了,那些数组是否会被迭代?(从本质上讲,这会降低性能,对吧?)
NiCk Newman

2
@NiCkNewman以及你以后引用对象infor ... in循环只会
尖尖的

我知道了。只是好奇,因为我在主游戏对象中有对象和数组,并且想知道是否对于局接而言,仅是索引上的常规for循环会更痛苦。
NiCk Newman

@NiCkNewman很好,整个问题的主题是您不应该for ... in在数组上使用;有很多很好的理由不这样做。与其说“确保它不会损坏”,不如说是一个性能问题。
尖尖的2015年

好吧,我的对象从技术上讲是存储在数组中的,这就是我担心的原因,例如:[{a:'hey',b:'hi'},{a:'hey',b:'hi'}],但是,我知道。
NiCk Newman

9

同样,由于语义,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"

9

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

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

  1. 它也会遍历继承的属性:首先,所有对的扩展Array.prototype都应使用进行,Object.defineProperty()并且其enumerable描述符应设置为false。任何不这样做的库都不应使用。
  2. 稍后添加到继承链中的属性将被计算在内:Object.setPrototypeOf通过Class或Class 进行数组子分类时extend。您应该再次使用Object.defineProperty()默认情况下将writableenumerableconfigurable属性描述符设置为的属性false。让我们在这里看到一个数组子分类的例子...

function Stack(...a){
  var stack = new Array(...a);
  Object.setPrototypeOf(stack, Stack.prototype);
  return stack;
}
Stack.prototype = Object.create(Array.prototype);                                 // now stack has full access to array methods.
Object.defineProperty(Stack.prototype,"constructor",{value:Stack});               // now Stack is a proper constructor
Object.defineProperty(Stack.prototype,"peak",{value: function(){                  // add Stack "only" methods to the Stack.prototype.
                                                       return this[this.length-1];
                                                     }
                                             });
var s = new Stack(1,2,3,4,1);
console.log(s.peak());
s[s.length] = 7;
console.log("length:",s.length);
s.push(42);
console.log(JSON.stringify(s));
console.log("length:",s.length);

for(var i in s) console.log(s[i]);

因此,您看到.. for in循环现在很安全,因为您关心代码。

  1. for in循环缓慢:该死的。如果要遍历稀疏数组,这是迄今为止最快的迭代方法。这是应该知道的最重要的性能技巧之一。让我们来看一个例子。我们将遍历一个稀疏数组。

var a = [];
a[0] = "zero";
a[10000000] = "ten million";
console.time("for loop on array a:");
for(var i=0; i < a.length; i++) a[i] && console.log(a[i]);
console.timeEnd("for loop on array a:");
console.time("for in loop on array a:");
for(var i in a) a[i] && console.log(a[i]);
console.timeEnd("for in loop on array a:");


@Ravi Shanker Reddy建立了良好的基准测试。正如我在回答中提到的那样,for in如果数组稀疏,则循环比其他“闪闪发光”,如果数组变大,则循环越闪闪发光。因此,我重新排列了一个稀疏数组的台式测试arr,大小为10000,其中只有50个项目是[42,"test",{t:1},null, void 0]从随机索引中随机选择的。您将立即注意到差异。->>在这里检查<<<-
Redu

8

除了其他问题,“ 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引擎以某种方式对其进行了优化,否则即使您使用了数字,它最终也会变成用于查找的字符串。
cHao 2012年

不管任何性能问题,如果您不熟悉JavaScript,请使用var i in a并期望索引为整数,然后执行类似的操作a[i+offset] = <value>将值放在完全错误的位置。(“ 1” +1 ==“ 11”)。
szmoore 2014年

8

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

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

myobject.propertyIsEnumerable('myproperty')

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

Object.getOwnPropertyDescriptor(myobject,'myproperty')

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


8

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]);

除非您使用原型对象,否则为true。;)下面
里卡多

Array也是Object因为
免费咨询2010年

2
for ... in与对象一起使用。没有自动检测之类的东西。
更好的Oliver

7

因为如果不小心,它将迭代原型链中对象所属的属性。

您可以使用for.. in,只需确保使用hasOwnProperty检查每个属性。


2
还不够-向数组实例添加任意命名的属性完全可以,并且可以true通过hasOwnProperty()检查进行测试。
尖尖的2010年

好点,谢谢。我从来没有足够愚蠢地自己对Array这样做,所以我没有考虑过!
日,日本

1
@Pointy我没有测试过,但是也许可以通过isNaN对每个属性名称进行检查来解决。
WynandB

1
@Wynand有趣的主意;但是我真的不明白为什么用一个简单的数字索引进行迭代如此简单是值得的。
Pointy 2013年

@WynandB对不起,但我感到有必要进行更正:isNaN用于检查变量是否为特殊值NaN,它不能用于检查“数字以外的事物”(可以与常规变量一起使用) typeof)。
doldt 2015年

6

这并不一定是坏的(根据你在做什么),但在阵列的情况下,如果有什么东西被添加到Array.prototype,那么你会得到奇怪的结果。您希望此循环运行三次的位置:

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

如果helpfulUtilityMethod已将调用的函数添加到Arrayprototype,那么你的循环将最终运行四次:key012,和helpfulUtilityMethod。如果您只期望整数,哎呀。


6

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


13
只是关于SO的注释-没有“之上”的注释,因为注释始终会更改页面上的顺序。因此,我们真的不知道您的意思是什么。出于这个原因,最好说“在x人的评论中”。
日航

@JAL ...或将永久链接添加到答案。
WynandB

5

使用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]

4

这是(通常)不好的做法的原因:

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

范例

Array.prototype.hithere = 'hithere';

var array = [1, 2, 3];
for (let el in array){
    // the hithere property will also be iterated over
    console.log(el);
}

  1. for...in循环不保证特定的迭代顺序。尽管这些天通常在大多数现代浏览器中都可以看到订单,但是仍然没有100%保证。
  2. for...in循环会忽略undefined数组元素,即尚未分配的数组元素。

例如

const arr = []; 
arr[3] = 'foo';   // resize the array to 4
arr[4] = undefined; // add another element with value undefined to it

// iterate over the array, a for loop does show the undefined elements
for (let i = 0; i < arr.length; i++) {
    console.log(arr[i]);
}

console.log('\n');

// for in does ignore the undefined elements
for (let el in arr) {
    console.log(arr[el]);
}


2

for ... in在使用JavaScript处理对象时很有用,但不适用于Array,但是我们仍然不能说这是错误的方法,但不建议这样做,建议在下面的示例中使用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对象!

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


2

for ... in循环始终枚举键。对象属性键始终为String,即使是数组的索引属性也是如此:

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


0

尽管此问题未专门解决,但我想补充一下,有一个很好的理由,不要在...中使用... NodeList(因为可以从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;失败了。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.