为什么使用“for...in”? 对于数组迭代是一个坏主意吗?

发布于 2024-07-13 05:00:17 字数 63 浏览 5 评论 0 原文

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

I've been told not to use for...in with arrays in JavaScript. Why not?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(28

若有似无的小暗淡 2024-07-20 05:00:17

原因是一个构造:

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

The reason is that one construct:

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

can sometimes be totally different from the other:

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

Also consider that JavaScript libraries might do things like this, which will affect any array you create:

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

月野兔 2024-07-20 05:00:17

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
}

正如我之前所说的,要迭代数组或类似数组的对象,最好的办法是使用 em>顺序循环,例如普通的 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
  }
}

The for-in statement by itself is not a "bad practice", however it can be mis-used, for example, to iterate over arrays or array-like objects.

The purpose of the for-in statement is to enumerate over object properties. This statement will go up in the prototype chain, also enumerating over inherited properties, a thing that sometimes is not desired.

Also, the order of iteration is not guaranteed by the spec., meaning that if you want to "iterate" an array object, with this statement you cannot be sure that the properties (array indexes) will be visited in the numeric order.

For example, in JScript (IE <= 8), the order of enumeration even on Array objects is defined as the properties were created:

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
}

Also, speaking about inherited properties, if you, for example, extend the Array.prototype object (like some libraries as MooTools do), that properties will be also enumerated:

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

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

As I said before to iterate over arrays or array-like objects, the best thing is to use a sequential loop, such as a plain-old for/while loop.

When you want to enumerate only the own properties of an object (the ones that aren't inherited), you can use the hasOwnProperty method:

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

And some people even recommend calling the method directly from Object.prototype to avoid having problems if somebody adds a property named hasOwnProperty to our object:

for (var prop in obj) {
  if (Object.prototype.hasOwnProperty.call(obj, prop)) {
    // prop is not inherited
  }
}
静谧幽蓝 2024-07-20 05:00:17

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

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

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

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

There are three reasons why you shouldn't use for..in to iterate over array elements:

  • for..in will loop over all own and inherited properties of the array object which aren't DontEnum; that means if someone adds properties to the specific array object (there are valid reasons for this - I've done so myself) or changed Array.prototype (which is considered bad practice in code which is supposed to work well with other scripts), these properties will be iterated over as well; inherited properties can be excluded by checking hasOwnProperty(), but that won't help you with properties set in the array object itself

  • for..in isn't guaranteed to preserve element ordering

  • it's slow because you have to walk all properties of the array object and its whole prototype chain and will still only get the property's name, ie to get the value, an additional lookup will be required

把昨日还给我 2024-07-20 05:00:17

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

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

这将写:

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

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

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

这将写:

0 = foo
1 = bar

Because for...in enumerates through the object that holds the array, not the array itself. If I add a function to the arrays prototype chain, that will also be included. I.e.

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

This will write:

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

And since you can never be sure that nothing will be added to the prototype chain just use a for loop to enumerate the array:

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

This will write:

0 = foo
1 = bar
两个我 2024-07-20 05:00:17

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

As of 2016 (ES6) we may use for…of for array iteration, as John Slegers already noticed.

I would just like to add this simple demonstration code, to make things clearer:

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++;
    }

The console shows:

for...of:

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

for...in:

0: 5
1: foo

In other words:

  • for...of counts from 0 to 5, and also ignores Array.prototype.foo. It shows array values.

  • for...in lists only the 5, ignoring undefined array indexes, but adding foo. It shows array property names.

野稚 2024-07-20 05:00:17

简短的回答:这不值得。


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


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

  • 使用 for (var property in array) 会导致 array 作为一个对象进行迭代,遍历对象原型链,最终执行速度比基于索引的 for 循环慢。
  • for (... in ...) 不保证按顺序返回对象属性,正如人们所期望的那样。
  • 使用 hasOwnProperty()!isNaN() 检查来过滤对象属性会产生额外的开销,导致其执行速度更慢,并且否定了首先使用它的关键原因地方,即因为格式更简洁。

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

Short answer: It's just not worth it.


Longer answer: It's just not worth it, even if sequential element order and optimal performance aren't required.


Long answer: It's just not worth it...

  • Using for (var property in array) will cause array to be iterated over as an object, traversing the object prototype chain and ultimately performing slower than an index-based for loop.
  • for (... in ...) is not guaranteed to return the object properties in sequential order, as one might expect.
  • Using hasOwnProperty() and !isNaN() checks to filter the object properties is an additional overhead causing it to perform even slower and negates the key reason for using it in the first place, i.e. because of the more concise format.

For these reasons an acceptable trade-off between performance and convenience doesn't even exist. There's really no benefit unless the intent is to handle the array as an object and perform operations on the object properties of the array.

勿挽旧人 2024-07-20 05:00:17

单独来看,在数组上使用 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 有一个未定义的值。

In isolation, there is nothing wrong with using for-in on arrays. For-in iterates over the property names of an object, and in the case of an "out-of-the-box" array, the properties corresponds to the array indexes. (The built-in propertes like length, toString and so on are not included in the iteration.)

However, if your code (or the framework you are using) add custom properties to arrays or to the array prototype, then these properties will be included in the iteration, which is probably not what you want.

Some JS frameworks, like Prototype modifies the Array prototype. Other frameworks like JQuery doesn't, so with JQuery you can safely use for-in.

If you are in doubt, you probably shouldn't use for-in.

An alternative way of iterating through an array is using a for-loop:

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

However, this have a different issue. The issue is that a JavaScript array can have "holes". If you define arr as:

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

Then the array have two items, but a length of 101. Using for-in will yield two indexes, while the for-loop will yield 101 indexes, where the 99 has a value of undefined.

携君以终年 2024-07-20 05:00:17

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

例如,

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

will write

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

while,

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

will write

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

当然,这可以通过包含

ii = parseInt(ii);

在循环中轻松克服,但第一个结构更直接。

In addition to the reasons given in other answers, you may not want to use the "for...in" structure if you need to do math with the counter variable because the loop iterates through the names of the object's properties and so the variable is a string.

For example,

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

will write

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

whereas,

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

will write

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

Of course, this can easily be overcome by including

ii = parseInt(ii);

in the loop, but the first structure is more direct.

泅人 2024-07-20 05:00:17

除了 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 循环。

Aside from the fact that for...in loops over all enumerable properties (which is not the same as "all array elements"!), see http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf, section 12.6.4 (5th edition) or 13.7.5.15 (7th edition):

The mechanics and order of enumerating the properties ... is not specified...

(Emphasis mine.)

That means if a browser wanted to, it could go through the properties in the order in which they were inserted. Or in numerical order. Or in lexical order (where "30" comes before "4"! Keep in mind all object keys -- and thus, all array indexes -- are actually strings, so that makes total sense). It could go through them by bucket, if it implemented objects as hash tables. Or take any of that and add "backwards". A browser could even iterate randomly and be ECMA-262 compliant, as long as it visited each property exactly once.

In practice, most browsers currently like to iterate in roughly the same order. But there's nothing saying they have to. That's implementation specific, and could change at any time if another way was found to be far more efficient.

Either way, for...in carries with it no connotation of order. If you care about order, be explicit about it and use a regular for loop with an index.

凝望流年 2024-07-20 05:00:17

主要有两个原因:

一个

正如其他人所说,您可能会得到不在数组中或从原型继承的键。 因此,如果,比方说,一个库向数组或对象原型添加了一个属性:

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 实现的,而不是作为数组。
这看起来是一件小事,但它确实会搞砸应用程序并且很难调试。

Mainly two reasons:

One

Like others have said, You might get keys which aren't in your array or that are inherited from the prototype. So if, let's say, a library adds a property to the Array or Object prototypes:

Array.prototype.someProperty = true

You'll get it as part of every array:

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

you could solve this with the hasOwnProperty method:

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

but this is true for iterating over any object with a for-in loop.

Two

Usually the order of the items in an array is important, but the for-in loop won't necessarily iterate in the right order, that's because it treats the array as an object, which is the way it is implemented in JS, and not as an array.
This seems like a small thing, but it can really screw up applications and is hard to debug.

深海不蓝 2024-07-20 05:00:17

我认为我没有太多要补充的,例如。 三联画的答案CMS 的回答为什么在某些情况下应避免使用for...in

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

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

注意:

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

I don't think I have much to add to eg. Triptych's answer or CMS's answer on why using for...in should be avoided in some cases.

I do, however, would like to add that in modern browsers there is an alternative to for...in that can be used in those cases where for...in can't be used. That alternative is for...of :

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

Note :

Unfortunately, no version of Internet Explorer supports for...of (Edge 12+ does), so you'll have to wait a bit longer until you can use it in your client side production code. However, it should be safe to use in your server side JS code (if you use Node.js).

无风消散 2024-07-20 05:00:17

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

Because it enumerates through object fields, not indexes. You can get value with index "length" and I doubt you want this.

王权女流氓 2024-07-20 05:00:17

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

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

The problem with for ... in ... — and this only becomes a problem when a programmer doesn't really understand the language; it's not really a bug or anything — is that it iterates over all members of an object (well, all enumerable members, but that's a detail for now). When you want to iterate over just the indexed properties of an array, the only guaranteed way to keep things semantically consistent is to use an integer index (that is, a for (var i = 0; i < array.length; ++i) style loop).

Any object can have arbitrary properties associated with it. There would be nothing terrible about loading additional properties onto an array instance, in particular. Code that wants to see only indexed array-like properties therefore must stick to an integer index. Code that is fully aware of what for ... in does and really need to see all properties, well then that's ok too.

滥情哥ㄟ 2024-07-20 05:00:17

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

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

  1. 它也会循环继承的属性:首先,对 Array.prototype 的任何扩展都应该通过使用 Object.defineProperty() 及其可枚举 描述符应设置为false。 任何不这样做的库根本不应该被使用。
  2. 稍后添加到继承链中的属性会被计数:通过 Object.setPrototypeOf 或 Class extend 进行数组子类化时。 您应该再次使用 Object.defineProperty() ,默认情况下将 writableenumerableconfigurable 属性描述符设置为。 让我们在这里看一个数组子类化示例......
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:");

TL&DR: Using the for in loop in arrays is not evil, in fact quite the opposite.

I think the for in loop is a gem of JS if used correctly in arrays. You are expected to have full control over your software and know what you are doing. Let's see the mentioned drawbacks and disprove them one by one.

  1. It loops through inherited properties as well: First of all any extensions to the Array.prototype should have been done by using Object.defineProperty() and their enumerable descriptor should be set to false. Any library not doing so should not be used at all.
  2. Properties those you add to the inheritance chain later get counted: When doing array sub-classing by Object.setPrototypeOf or by Class extend. You should again use Object.defineProperty() which by default sets the writable, enumerable and configurable property descriptors to false. Lets see an array sub-classing example here...

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

So you see.. for in loop is now safe since you cared about your code.

  1. The for in loop is slow: Hell no. It's by far the fastest method of iteration if you are looping over sparse arrays which are needed time to time. This is one of the most important performance tricks that one should know. Let's see an example. We will loop over a sparse array.

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:");

森林迷了鹿 2024-07-20 05:00:17

此外,由于语义的原因,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"

Also, due to semantics, the way for, in treats arrays (i.e. the same as any other JavaScript object) is not aligned with other popular languages.

// 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"
怼怹恏 2024-07-20 05:00:17

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

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

Here are the reasons why this is (usually) a bad practice:

  1. for...in loops iterate over all their own enumerable properties and the enumerable properties of their prototype(s). Usually in an array iteration we only want to iterate over the array itself. And even though you yourself may not add anything to the array, your libraries or framework might add something.

Example:

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 loops do not guarantee a specific iteration order. Although is order is usually seen in most modern browsers these days, there is still no 100% guarantee.
  2. for...in loops ignore undefined array elements, i.e. array elements which not have been assigned yet.

Example::

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

金兰素衣 2024-07-20 05:00:17

除了其他问题之外,“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'

In addition to the other problems, the "for..in" syntax is probably slower, because the index is a string, not an integer.

var a = ["a"]
for (var i in a)
    alert(typeof i)  // 'string'
for (var i = 0; i < a.length; i++)
    alert(typeof i)  // 'number'
眉黛浅 2024-07-20 05:00:17

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

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

myobject.propertyIsEnumerable('myproperty')

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

Object.getOwnPropertyDescriptor(myobject,'myproperty')

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

An important aspect is that for...in only iterates over properties contained in an object which have their enumerable property attribute set to true. So if one attempts to iterate over an object using for...in then arbitrary properties may be missed if their enumerable property attribute is false. It is quite possible to alter the enumerable property attribute for normal Array objects so that certain elements are not enumerated. Though in general the property attributes tend to apply to function properties within an object.

One can check the value of a properties' enumerable property attribute by:

myobject.propertyIsEnumerable('myproperty')

Or to obtain all four property attributes:

Object.getOwnPropertyDescriptor(myobject,'myproperty')

This is a feature available in ECMAScript 5 - in earlier versions it was not possible to alter the value of the enumerable property attribute (it was always set to true).

暖阳 2024-07-20 05:00:17

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

The for/in works with two types of variables: hashtables (associative arrays) and array (non-associative).

JavaScript will automatically determine the way its passes through the items. So if you know that your array is really non-associative you can use for (var i=0; i<=arrayLen; i++), and skip the auto-detection iteration.

But in my opinion, it's better to use for/in, the process required for that auto-detection is very small.

A real answer for this will depend on how the browser parsers/interpret the JavaScript code. It can change between browsers.

I can't think of other purposes to not using 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]);
十雾 2024-07-20 05:00:17

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

您可以使用 for.. in,只需确保使用 拥有自己的属性

Because it will iterate over properties belonging to objects up the prototype chain if you're not careful.

You can use for.. in, just be sure to check each property with hasOwnProperty.

筱武穆 2024-07-20 05:00:17

这不一定是坏事(根据你正在做的事情),但对于数组来说,如果某些内容被添加到 Array.prototype 中,那么你就会得到奇怪的结果。 您希望此循环运行三次的位置:

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

如果名为 helpfulUtilityMethod 的函数已添加到 Arrayprototype 中,则您的循环最终会运行四次:key 将为 012helpfulUtilityMethod< /代码>。 如果您只期望整数,哎呀。

It's not necessarily bad (based on what you're doing), but in the case of arrays, if something has been added to Array.prototype, then you're going to get strange results. Where you'd expect this loop to run three times:

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

If a function called helpfulUtilityMethod has been added to Array's prototype, then your loop would end up running four times: key would be 0, 1, 2, and helpfulUtilityMethod. If you were only expecting integers, oops.

Oo萌小芽oO 2024-07-20 05:00:17

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

You should use the for(var x in y) only on property lists, not on objects (as explained above).

素手挽清风 2024-07-20 05:00:17

对数组使用 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]

Using the for...in loop for an array is not wrong, although I can guess why someone told you that:

1.) There is already a higher order function, or method, that has that purpose for an array, but has more functionality and leaner syntax, called 'forEach': Array.prototype.forEach(function(element, index, array) {} );

2.) Arrays always have a length, but for...in and forEach do not execute a function for any value that is 'undefined', only for the indexes that have a value defined. So if you only assign one value, these loops will only execute a function once, but since an array is enumerated, it will always have a length up to the highest index that has a defined value, but that length could go unnoticed when using these loops.

3.) The standard for loop will execute a function as many times as you define in the parameters, and since an array is numbered, it makes more sense to define how many times you want to execute a function. Unlike the other loops, the for loop can then execute a function for every index in the array, whether the value is defined or not.

In essence, you can use any loop, but you should remember exactly how they work. Understand the conditions upon which the different loops reiterate, their separate functionalities, and realize they will be more or less appropriate for differing scenarios.

Also, it may be considered a better practice to use the forEach method than the for...in loop in general, because it is easier to write and has more functionality, so you may want to get in the habit of only using this method and standard for, but your call.

See below that the first two loops only execute the console.log statements once, while the standard for loop executes the function as many times as specified, in this case, 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]
千纸鹤带着心事 2024-07-20 05:00:17

for...in 循环始终枚举键。
对象属性键始终是 String,甚至是数组的索引属性:

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

A for...in loop always enumerates the keys.
Objects properties keys are always String, even the indexed properties of an array :

var myArray = ['a', 'b', 'c', 'd'];
var total = 0
for (elem in myArray) {
  total += elem
}
console.log(total); // 00123
说不完的你爱 2024-07-20 05:00:17

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 

正如你所看到的结果一样......

但是让我们尝试一些东西,让我们原型化一些东西 < strong>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

你看到someoneelse!!!...在这种情况下我们实际上循环遍历新的Array对象!

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

for...in is useful when working on an object in JavaScript, but not for an Array, but still we can not say it's a wrong way, but it's not recommended, look at this example below using for...in loop:

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

OK, let's do it with Array now:

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

As you see the result the same...

But let's try something, let's prototype something to Array...

Array.prototype.someoneelse = "someoneelse";

Now we create a new 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

You see the someoneelse!!!... We actually looping through new Array object in this case!

So that's one of the reasons why we need to use for..in carefully, but it's not always the case...

慢慢从新开始 2024-07-20 05:00:17

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

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

Since JavaScript elements are saved as standard object properties, it
is not advisable to iterate through JavaScript arrays using for...in
loops because normal elements and all enumerable properties will be
listed.

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

如果没有你 2024-07-20 05:00:17

虽然这个问题没有具体解决,但我想补充一点,有一个很好的理由:不要将 for...in 与 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 states) node.href 。 = newLink; 失败了。

although not specifically addressed by this question, I would add that there's a very good reason not to ever use for...in with a NodeList (as one would obtain from a querySelectorAll call, as it doesn't see the returned elements at all, instead iterating only over the NodeList properties.

in the case of a single result, I got:

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

which explained why my for (node in nodes) node.href = newLink; was failing.

溺孤伤于心 2024-07-20 05:00:17

for in 循环在遍历数组时将索引转换为字符串。
例如,在下面的代码中,在用 i+1 初始化 j 的第二个循环中,i 是索引,但在字符串中(“0”、“1”等),而 js 中的 number + string 是字符串。 如果js遇到“0”+1,它将返回“01”。

var maxProfit = function(prices) {
  let maxProfit = 0;
  for (let i in prices) {
    for (let j = i + 1; j < prices.length; j++) {
      console.log(prices[j] - prices[i], "i,j", i, j, typeof i, typeof j);
      if ((prices[j] - prices[i]) > maxProfit) maxProfit = (prices[j] - prices[i]);
    }
  }
  return maxProfit;
};

maxProfit([7, 1, 5, 3, 6, 4]);

for in loop converts the indices to string when traversing through an array.
For example, In the below code, in the second loop where initialising j with i+1, i is the index but in a string ("0", "1" etc) and number + string in js is a string. if js encounters "0" + 1 it will return "01".

var maxProfit = function(prices) {
  let maxProfit = 0;
  for (let i in prices) {
    for (let j = i + 1; j < prices.length; j++) {
      console.log(prices[j] - prices[i], "i,j", i, j, typeof i, typeof j);
      if ((prices[j] - prices[i]) > maxProfit) maxProfit = (prices[j] - prices[i]);
    }
  }
  return maxProfit;
};

maxProfit([7, 1, 5, 3, 6, 4]);

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文