javascript - length - while loop 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中實現的方式,而不是作為一個數組。 這似乎是一件小事,但它可能會使應用程序變得非常糟糕,而且很難調試。


儘管我可以猜出為什麼有人告訴你: 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]

原因是一個構造:

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


因為如果你不小心的話,它會迭代屬於原型鏈上的對象的屬性。

您可以使用for.. in ,只要確保使用hasOwnProperty檢查每個屬性。


因為它通過對象字段枚舉,而不是索引。 你可以通過索引“長度”獲得價值,我懷疑你想要這個。


不一定是壞的(根據你在做什麼),但在數組的情況下,如果已經添加了Array.prototype ,那麼你將得到奇怪的結果。 你希望這個循環運行三次:

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

如果一個名為helpfulUtilityMethod的函數已經添加到Arrayprototype ,那麼您的循環最終將運行四次: key將是0helpfulUtilityMethod 。 如果你只是期待整數,哎呀。


我認為我沒有太多補充,例如。 在某些情況下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”語法可能更慢,因為索引是一個字符串,而不是一個整數。

var a = ["a"]
for (var i in a)
    alert(typeof i)  // 'string'
for (var i = 0; i < a.length; i++)
    alert(typeof i)  // 'number'




for-loop