javascript - js遍历字典 - node js数组遍历




为什么在数组迭代中使用“for... in”是一个坏主意? (16)

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

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

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


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

任何对象都可以有与其相关的任意属性。 特别是将附加属性加载到数组实例上没有什么可怕的。 因此,只想查看索引数组类属性的代码必须坚持一个整数索引。 代码完全知道什么for ... in并且确实需要查看所有属性,那么也可以。


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

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

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

  1. 它通过继承的属性循环:首先,任何对Array.prototype扩展都应该使用Object.defineProperty()完成,并且它们的enumerable描述符应该设置为false 。 任何不这样做的图书馆都不应该使用。
  2. 以后添加到继承链的属性会被计算在内:当通过Object.setPrototypeOf或Class Object.setPrototypeOf进行数组子类化时。 您应该再次使用Object.defineProperty() ,它默认将writableenumerableenumerable configurable属性描述符设置为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:");


一个重要的方面是, for...in只需对包含在具有enumerable 属性属性设置为true的对象中的属性进行迭代。 所以如果试图迭代一个使用for...in的对象for...in那么任意属性可能会被忽略,如果它们的enumerable属性为false的话。 很有可能改变普通数组对象的enumerable属性,以便某些元素不被枚举。 尽管通常属性属性倾向于适用于对象内的函数属性。

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

myobject.propertyIsEnumerable('myproperty')

或者获取全部四个属性属性:

Object.getOwnPropertyDescriptor(myobject,'myproperty')

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


主要有两个原因:

像其他人所说的那样,您可能会得到不在阵列中或从原型继承的密钥。 因此,如果我们说一个库向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中实现的方式,而不是作为一个数组。 这似乎是一件小事,但它可能会使应用程序变得非常糟糕,而且很难调试。


原因是一个构造:

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
*/


因为...在枚举通过持有该数组的对象,而不是数组本身。 如果我向数组原型链添加一个函数,那么也会包含这个函数。 即

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没有任何问题。 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-loop将生成101个索引,其中99的值为undefined


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

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

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

3.)循环的标准将执行一次函数,与您在参数中定义的次数一样多,并且由于数组是数字编号的,因此定义要执行一次函数的次数会更有意义。 与其他循环不同,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]

我认为我没有太多补充,例如。 在某些情况下for-in应该避免使用三联公司的答案CMS的回答

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

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

注意 :

不幸的是,没有Internet Explorer版本支持此功能( Edge 12+ ),因此您需要等一会,直到您可以在客户端生产代码中使用它。 但是,在您的服务器端JS代码中使用应该是安全的(如果您使用Node.js )。


有三个原因让你不应该使用for..in遍历数组元素:

  • for..infor..in不是DontEnum的数组对象的所有自己的和继承的属性; 这意味着如果某人向特定的数组对象添加了属性(这里有合理的理由 - 我自己也这么做了),或者改变了Array.prototype (在代码中被认为是不好的做法,它应该与其他脚本一起工作),这些属性也会迭代; 继承的属性可以通过检查hasOwnProperty()来排除,但这不会帮助你使用数组对象本身的属性

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

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


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

较长的回答:即使不需要顺序元素顺序和最佳性能,它也不值得。

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

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

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


除了...... 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 ......来说都没有秩序的内涵。 如果你关心的是顺序,请明确说明它,并使用一个带有索引的常规循环。


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

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





for-loop