为什么在数组迭代中使用"for ... in"是一个坏主意?

lYr*_*sSH 1761 javascript arrays iteration loops for-loop

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

Tri*_*ych 1511

原因是一个结构:

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
*/
Run Code Online (Sandbox Code Playgroud)

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

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
*/
Run Code Online (Sandbox Code Playgroud)

还要考虑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
*/
Run Code Online (Sandbox Code Playgroud)

  • 记得使用`(var x in a)`而不是`(x in a)` - 不想创建全局. (392认同)
  • 从历史上看,有些浏览器甚至迭代过'length','toString'等等! (138认同)
  • @Stewart:**JS中的所有对象**都是关联的.JS Array是一个对象,所以是的,它也是关联的,但这不是它的用途.如果要迭代对象的_keys_,请使用`for(对象中的var键)`.但是,如果要迭代数组的_elements_,请使用`for(var i = 0; i <array.length; i + = 1)`. (85认同)
  • 第一个问题不是它不好的原因,只是语义上的差异.第二个问题在我看来是一个原因(除了库之间的冲突之外)改变内置数据类型的原型是坏的,而不是for..in是坏的. (76认同)
  • 你说的第一个例子,它*迭代数字索引从0到4,因为每个人都期望*,我**期望它从0迭代到5**!因为如果在位置5中添加元素,则该数组将具有6个元素(其中5个未定义). (41认同)
  • 我认为如果你将[5]设置为5以外的其他东西,例如a [5] = 42,那么这些例子会更清晰.第二个例子("for(x in a)"中只列举了一个值这一事实对我来说并不奇怪;它枚举的值为5而不是42的事实让我感到惊讶(来自其他类似构造的语言)枚举列表内容而不是数组索引). (21认同)
  • 这总体上呈现出javascript中的问题,因为类似for..in的观点是对对象或数组的关键不可知.恰恰相反,如果你向数组[5]添加一些东西,你希望数组有一个项目,键5,而不是你没有插入的五个空白项目.你希望数组中有你放入的内容!除非你明确告诉数组有五个项目,否则键'5'实际上不应该表示数组中的项目数.但这与数组所代表的内容有关; 在JSON数组中没有真正的密钥; 使用密钥一词具有误导性. (8认同)
  • JS中的数组是关联的 - 它们只能用作线性数组.因此,使用for..in的重点是迭代数组的键,无论它们是什么. (4认同)
  • 为什么 `for...in` 不迭代所有标准原型,而不仅仅是像 foo 那样创建新的 `prototype`? (4认同)
  • @斯图尔特。“语义差异”实际上是 Javascript 中的一个缺陷。破碎的语义意味着代码不会按照您的想法执行,这就是通常避免使用该构造的原因。 (3认同)
  • @stivlo - 如果你只在第5位添加一个元素,那么数组将有一个元素,因为`for..in`循环显示.不存在的元素之间存在差异,因此当您尝试访问它时返回"undefined"和存在的元素并返回"undefined",因为该值已被显式赋值. (3认同)
  • 你错过了它不会以正确的顺序迭代的事实:http://stackoverflow.com/a/21558566/1233630 (3认同)
  • 我将 `for(int i=0` 更正为 `for(var i=0`)。第一个版本产生了一个不太有帮助的“缺少分号”错误(至少在 Firefox 中)。 (2认同)
  • 那么使用内置数组作为关联数组会滥用JS对象模型吗?是否有任何关于用于AA的官方建议? (2认同)
  • 在第一个例子中.. 在循环之前只_call_ a.length 一次(并设置为 var)会更有效吗?vs每次循环迭代? (2认同)
  • 奥古斯塔姆 - 是的,它会更有效率.但它也将是更多的代码,因此将更直接地说明所讨论的概念. (2认同)
  • `Object.keys(a).forEach( function(item) { console.log(item) } )` 只接受真实的属性,而不是原型中设置的属性。 (2认同)
  • @Martijin:ES6 标准中有一个新的“for let value of object”函数,而不是相当长和冗长的 `for(var i = 0; i &lt; array.length; i += 1)` 模式,它是大多数现代浏览器已经支持 [参见 MDN 页面](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of) (2认同)
  • @ Jonathan002我也想知道。因此,“ for ... in”仅在[**可枚举的属性**](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Enumerability_and_ownership_of_properties)上进行迭代。Array.prototype方法是使用[`Object.defineProperty`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#Description)定义的默认情况下,“可枚举”为“假”。如果要使用Object.defineProperty(Array.prototype,'foo',{value:1})),则不会显示`foo` (2认同)
  • 这个答案很好地解释了为什么不使用“for ... in”作为迭代未完全定义的数组的方法,但这只是一个(相当特殊的)边缘情况。它根本没有解释为什么它不应该被使用_一般_或者它有什么不好_一般_。答案同样可以重写为“如果您使用它迭代部分声明的数组,它将只返回声明的部分”,而不会丢失任何值或信息。 (2认同)

CMS*_*CMS 387

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
}
Run Code Online (Sandbox Code Playgroud)

另外,谈到继承的属性,例如,如果你扩展Array.prototype对象(就像MooTools那样的一些库),那么也会枚举这些属性:

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

for (var p in []) { // an empty array
  // last will be enumerated
}
Run Code Online (Sandbox Code Playgroud)

正如我之前所说,迭代数组或类似数组的对象,最好的方法是使用顺序循环,例如普通的for/ while循环.

如果只想枚举对象的自身属性(未继承的属性),可以使用以下hasOwnProperty方法:

for (var prop in obj) {
  if (obj.hasOwnProperty(prop)) {
    // prop is not inherited
  }
}
Run Code Online (Sandbox Code Playgroud)

有些人甚至建议直接调用该方法,Object.prototype以避免遇到问题,如果有人添加了一个名为hasOwnProperty我们的对象的属性:

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

  • 关于"hasOwnProperty"的最后一点的问题:如果有人在某个对象上覆盖"hasOwnProperty",那么你就会遇到问题.但如果有人覆盖"Object.prototype.hasOwnProperty",你不会遇到同样的问题吗?无论哪种方式,他们都搞砸了你,这不是你的责任吗? (17认同)
  • 另请参阅David Humphrey的帖子[快速迭代JavaScript中的对象](http://vocamus.net/dave/?p=1205) - 对于数组的`for..in`比"普通"循环要慢得多. (10认同)
  • @ScottRippey:如果你想把它带到那里:https://www.youtube.com/watch?v = FrFUI591WhI (4认同)

Chr*_*oph 115

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

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

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

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


Pim*_*ger 52

因为...枚举通过保存数组的对象,而不是数组本身.如果我向数组原型链添加一个函数,那么它也将被包含在内.即

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

这将写:

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

因为你永远无法确定什么都不会被添加到原型链中,只需使用for循环来枚举数组:

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

这将写:

0 = foo
1 = bar

  • 数组**是**对象,没有"保存数组的对象". (15认同)

Jac*_*esB 40

孤立地,在数组上使用for-in没有任何问题.For-in迭代对象的属性名称,对于"开箱即用"数组,属性对应于数组索引.(内置属性如length,toString等等不包含在迭代中.)

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

一些JS框架,如Prototype,修改了Array原型.像JQuery这样的其他框架没有,所以使用JQuery可以安全地使用for-in.

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

迭代数组的另一种方法是使用for循环:

for (var ix=0;ix<arr.length;ix++) alert(ix);
Run Code Online (Sandbox Code Playgroud)

但是,这有一个不同的问题.问题是JavaScript数组可能有"漏洞".如果您定义arr为:

var arr = ["hello"];
arr[100] = "goodbye";
Run Code Online (Sandbox Code Playgroud)

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


Mar*_*rcG 33

截至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++;
    }
Run Code Online (Sandbox Code Playgroud)

控制台显示:

for...of:

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

for...in:

0: 5
1: foo
Run Code Online (Sandbox Code Playgroud)

换一种说法:

  • for...of计数从0到5,也忽略Array.prototype.foo.它显示了数组.

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


小智 31

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

例如,

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

将会写

0, number, 1
1, number, 2
...
Run Code Online (Sandbox Code Playgroud)

然而,

for (var ii in a) {
    document.write(i + ', ' + typeof i + ', ' + i+1);
}
Run Code Online (Sandbox Code Playgroud)

将会写

0, string, 01
1, string, 11
...
Run Code Online (Sandbox Code Playgroud)

当然,这可以通过包括来轻松克服

ii = parseInt(ii);
Run Code Online (Sandbox Code Playgroud)

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

  • 您可以使用前缀`+`而不是`parseInt`,除非您确实需要整数或忽略无效字符. (6认同)
  • @Derek朕会功夫 - 你绝对可以使用`parseInt`.问题是如果你不包括基数,旧的浏览器可能会尝试解释数字(因此025变为八进制).这在ECMAScript 5中得到修复,但对于以"0x"开头的数字仍然会发生(它将数字解释为十六进制).为了安全起见,使用基数来指定数字,例如`parseInt("025",10)` - 指定基数10. (6认同)

Wyn*_*ndB 28

简短回答:这不值得.


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


答案很长:这不值得,原因如下:

  • 使用for (var property in array)将导致'array'被解释为任何其他对象,遍历对象属性链并最终执行比基于索引的array循环慢.
  • 它不能保证按照人们的预期按顺序返回对象属性.
  • 使用forfor (... in ...)检查过滤对象属性是一个额外的开销,导致它执行(甚至更多)更慢.此外,引入这样的附加逻辑否定了首先使用它的关键原因,即由于更简洁的格式.

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


cHa*_*Hao 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带索引的常规循环.


Lio*_*ior 18

主要有两个原因:

像其他人所说的那样,你可能会获得不在你的数组中或从原型继承的键.因此,如果,库,则会向Array或Object原型添加属性:

Array.prototype.someProperty = true
Run Code Online (Sandbox Code Playgroud)

你将它作为每个数组的一部分:

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

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

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

但是对于使用for-in循环迭代任何对象都是如此.

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

  • `Object.keys(a).forEach(function(item){console.log(item)})`遍历一个自己的属性键数组,而不是那些从原型继承的属性键. (2认同)
  • 是的,但是像for-in循环一样,它不一定是正确的索引顺序.此外,它不适用于不支持ES5的旧版浏览器. (2认同)

vav*_*ava 16

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

  • 这个答案是对的."lenght"不会包含在for-in迭代中.它只包含您自己添加的属性. (5认同)
  • for(var i = 0; i <arr.length; i ++){} (3认同)
  • 在firefox 3中你也可以使用arr.forEach或for(var [i,v] in Iterator(arr)){}但这些都不适用于IE,尽管你可以自己编写forEach方法. (3认同)

Poi*_*nty 15

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

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

  • @NiCkNewman你在'for ... in`循环中的`in`之后引用的对象就是这样 (2认同)

Joh*_*ers 14

我认为我没有太多要补充,例如.在某些情况下,应避免使用Triptych的答案CMS对其使用原因的回答for...in.

但是,我想在现代浏览器中添加一个替代方案for...in,可以在不能使用的情况下for...in使用.那个替代方案是for...of:

for (var item of items) {
    console.log(item);
}
Run Code Online (Sandbox Code Playgroud)

注意 :

遗憾的是,没有任何版本的Internet Explorer支持此功能(Edge 12+确实如此),因此您必须等待一段时间才能在客户端生产代码中使用它.但是,在服务器端JS代码中使用它应该是安全的(如果使用Node.js).


mat*_*pop 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"
Run Code Online (Sandbox Code Playgroud)


Red*_*edu 9

TL&DR:for in在阵列中使用循环并不是邪恶的,实际上恰恰相反.

我认为for in如果在数组中正确使用,循环是JS的宝石.您需要完全控制您的软件并知道您在做什么.让我们看看上面提到的缺点并逐一反驳.

  1. 它也循环遍历继承的属性:首先,Array.prototype应该通过使用完成任何扩展,并且应该将Object.defineProperty()它们的enumerable描述符设置为false.任何不这样做的图书馆都不应该被使用.
  2. 您添加到继承链的属性稍后会被计算:Object.setPrototypeOf按类或按类进行数组子类化时extend.您应该再次使用Object.defineProperty()其默认设置writable,enumerableconfigurable属性描述到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]);
Run Code Online (Sandbox Code Playgroud)

所以你看到.. 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:");
Run Code Online (Sandbox Code Playgroud)


小智 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]);
Run Code Online (Sandbox Code Playgroud)

  • `for ... in`适用于对象.没有自动检测这样的东西. (2认同)

dc1*_*dc1 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'
Run Code Online (Sandbox Code Playgroud)


Pie*_*erz 8

一个重要的方面是for...in只迭代对象中包含的属性,这些属性枚举 属性属性设置为true.因此,如果尝试使用对象进行迭代,for...in则如果其可枚举属性属性为false,则可能会遗漏任意属性.很可能更改普通Array对象的enumerable属性属性,以便不枚举某些元素.虽然通常属性属性倾向于应用于对象内的函数属性.

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

myobject.propertyIsEnumerable('myproperty')
Run Code Online (Sandbox Code Playgroud)

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

Object.getOwnPropertyDescriptor(myobject,'myproperty')
Run Code Online (Sandbox Code Playgroud)

这是ECMAScript 5中提供的一项功能 - 在早期版本中,无法更改enumerable属性的值(它始终设置为true).


JAL*_*JAL 7

因为如果你不小心的话,它将迭代属于原型链上的对象的属性.

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

  • 还不够 - 向数组实例添加任意命名属性是完全可以的,那些将从`hasOwnProperty()`检查中测试`true`. (2认同)

jos*_*736 6

不一定是坏的(根据你正在做的事情),但在数组的情况下,如果添加了某些东西Array.prototype,那么你将得到奇怪的结果.你希望这个循环运行三次:

var arr = ['a','b','c'];
for (var key in arr) { ... }
Run Code Online (Sandbox Code Playgroud)

如果被叫功能helpfulUtilityMethod已经被添加到Arrayprototype,那么你的循环将最终运行四次:key0,1,2,和helpfulUtilityMethod.如果你只是期望整数,哎呀.


use*_*396 6

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

  • 只是关于SO的说明 - 没有"上方",因为评论会一直改变页面上的顺序.所以,我们真的不知道你的意思.出于这个原因,可以说"在x人的评论中". (13认同)

Wil*_*een 6

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

  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);
}
Run Code Online (Sandbox Code Playgroud)

  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]);
}
Run Code Online (Sandbox Code Playgroud)


小智 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]
Run Code Online (Sandbox Code Playgroud)