我被告知在JavaScript中不要使用for...in
循环遍历数组。为什么?
我被告知在JavaScript中不要使用for...in
循环遍历数组。为什么?
我认为对于如 Triptych 的回答或 CMS 的回答中所述,关于在某些情况下应避免使用for...in
, 我没有太多要补充的。
然而,我想要补充的是,在现代浏览器中,有一种可以替代for...in
并可在for...in
无法使用的情况下使用的方法。这种方法是使用for...of
:
for (var item of items) {
console.log(item);
}
很遗憾,任何版本的Internet Explorer都不支持for...of
(Edge 12+可以),因此你需要等待一段时间才能在客户端生产代码中使用它。然而,在服务器端JS代码中使用它应该是安全的(如果您使用Node.js)。
for...in...
——只有当程序员不真正理解语言时,才会出现这个问题;这不是一个真正的错误或任何其他东西——它遍历对象的所有成员(好吧,所有可枚举成员,但现在不需要细节)。 当您想要迭代仅数组的索引属性时,保持语义一致的唯一保证方式是使用整数索引(也就是说,像 for(var i = 0; i < array.length; ++i)
这样的循环)。
任何对象都可以具有与其关联的任意属性。 尤其是将其他属性加载到数组实例上并没有什么可怕的事情。希望仅查看索引数组属性的代码因此必须坚持整数索引。 完全了解 for ... in
的工作原理和真正需要查看所有属性的代码,那么也可以。
for in
,相比于常规的for循环,这些数组会被迭代吗?(这本质上会降低性能,对吧?) - NiCk Newmanfor ... in
循环中,你引用 in
后面的对象将只是一个简单的迭代器。 - Pointyfor ... in
循环;有许多很好的理由不这样做。这不仅仅是性能问题,更是“确保它不会出错”的问题。 - Pointy[{a:'嗨',b:'你好'},{a:'嗨',b:'你好'}]
,但是我理解了。 - NiCk Newmanfor in
循环不是邪恶的行为,事实上相反。for in
循环是JS的一个宝石。您可以完全控制您的软件并知道自己在做什么。让我们看看提到的缺点并逐一反驳它们。
Array.prototype
的扩展都应该使用Object.defineProperty()
进行,并且它们的enumerable
描述符应设置为false
。任何没有这样做的库都不应该被使用。Object.setPrototypeOf
或Class extend
进行数组子类化时。您应该再次使用Object.defineProperty()
,默认情况下将writable
、enumerable
和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
循环现在是安全的,因为你关心你的代码。
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
循环遍历它们自己所有可枚举的属性以及它们的原型的可枚举属性。在数组迭代中,我们通常只想遍历数组本身。即使您自己不向数组添加任何内容,您的库或框架可能会添加一些东西。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);
}
for...in
循环不保证特定的迭代顺序。尽管这个顺序在大多数现代浏览器中是可见的,但仍不能100%地保证。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]);
}
另外,由于语义的原因,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"
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
适用于对象。并不存在自动检测的情况。 - a better oliverfor...in
只会迭代对象中那些其可枚举属性设置为 true 的属性。因此,如果尝试使用 for...in
迭代一个对象,则在其可枚举属性设置为 false 时,可能会错过任意属性。可以改变普通数组对象的可枚举属性,以便不枚举某些元素,但通常情况下,属性属性适用于对象内的函数属性。myobject.propertyIsEnumerable('myproperty')
或者获取所有四个属性:
Object.getOwnPropertyDescriptor(myobject,'myproperty')
这是 ECMAScript 5 中提供的一个功能 - 在早期版本中,无法更改可枚举属性的值(它始终设置为 true)。
var a = ["a"]
for (var i in a)
alert(typeof i) // 'string'
for (var i = 0; i < a.length; i++)
alert(typeof i) // 'number'
var i in a
并期望索引是一个整数,那么像 a[i+offset] = <value>
这样的操作将会把值放在完全错误的位置。("1" + 1 == "11")。 - szmoorefor..in
,但一定要使用hasOwnProperty检查每个属性。hasOwnProperty()
检查测试为 true
。 - PointyisNaN
检查来克服这个问题。 - WynandBisNaN
用于检查变量是否为特殊值 NaN,不能用于检查“非数字的内容”(你可以使用常规 typeof 进行检查)。 - doldt
var i = hCol1.length; for (i;i;i--;) {}
,将 i 缓存起来会有所不同,并简化测试。对于老版本的浏览器而言,for
和while
的差异越大,因此请始终缓存 "i" 计数器。当然,负数并不总是适用于所有情况,并且while
在某些人看来会使代码更加难懂。另外,请注意var i = 1000; for (i; i; i--) {}
和var b =1000 for (b; b--;) {}
这两种写法,其中 i 从 1000 到 1,b 从 999 到 0。对于老版本的浏览器而言,while
倾向于使用for
以提高性能。 - Mark Schultheissfor(var i = 0, l = myArray.length; i < l; ++i) ...
是使用顺序迭代时最快和最好的选择。 - Mathieu Amiot