實例解析forEach、for...in與for...of

在開發過程中經常需要循環遍歷數組或者對象,js也爲我們提供了不少方法供使用,其中就有三兄弟forEach、for...in、for...of,這三個方法應該是使用頻率最高的,但很多人卻一值傻傻分不清,經常該混淆了它們的功能和注意點。就在今天,我來給它們一個大區分(*・ω< )。

forEach

forEach() 方法對數組的每個元素執行一次提供的函數。

從ES5開始,Javascript內置了forEach方法,用來遍歷數組 。

var arr = ['a', 'b', 'c', 'd', 'e'];

arr.forEach(function(item) {
  console.log(item); // a,b,c,d,e
});

!注意:forEach方法沒辦法使用 break 語句跳出循環,或者使用return從函數體內返回。

 

for...in

for...in語句以任意順序遍歷一個對象的可枚舉屬性。對於每個不同的屬性,語句都會被執行。

語法:

for (variable in object) {...}

variable 在每次迭代時,將不同的屬性名分配給變量。

object 被迭代枚舉其屬性的對象。

// 例子一
let obj = {a: '1', b: '2', c: '3', d: '4'};
for (let o in obj) {
    console.log(o)    // 遍歷的實際上是對象的屬性名稱 a,b,c,d
    console.log(obj[o])  // 這個纔是屬性對應的值1,2,3,4
}


// 例子二
Object.prototype.objName = 'objName '; 
var obj = {a: '1', b: '2', c: '3', d: '4'};
for (let o in obj) {
    console.log(o)    // a,b,c,d,objName 
}

for...in 循環只遍歷可枚舉屬性。像 Array和 Object使用內置構造函數所創建的對象都會繼承自Object.prototype和String.prototype的不可枚舉屬性,例如 String 的 indexOf()  方法或 Object的toString()方法。循環將遍歷對象本身的所有可枚舉屬性,以及對象從其構造函數原型中繼承的屬性(更接近原型鏈中對象的屬性覆蓋原型屬性)。 

! 注意:建議不使用for...in去迭代一個Array。因爲設計之初,是給普通以字符串的值爲key的對象使用的,而非數組。

數組索引只是具有整數名稱的枚舉屬性,並且與通用對象屬性相同。使用不能保證for...in將以任何特定的順序返回索引。因爲迭代的順序是依賴於執行環境的,所以數組遍歷不一定按次序訪問元素。因此當迭代訪問順序很重要的數組時,最好用整數索引去進行for循環(或者使用 Array.prototype.forEach() 或 for...of 循環)。

對於for...in的循環,可以由break,throw終止。

var obj = {a: '1', b: '2', c: '3', d: '4'}
for (let o in obj) {
	if(o=='c'){
        break;
    }
    console.log(o);
}

// 輸出: a,b

 

for...of

for...of 語句在可迭代對象(包括 Array,Map,Set,String,TypedArray,arguments 對象等等)上創建一個迭代循環,調用自定義迭代鉤子,併爲每個不同屬性的值執行語句。

語法:

for (variable of iterable) {
  //statements
}

variable 在每次迭代中,將不同屬性的值分配給變量。

iterable 被迭代枚舉其屬性的對象。

迭代數組Array

let iterable = [10, 20, 30];
for (let value of iterable) {
  console.log(value);
}
// 10
// 20
// 30

迭代字符串String

let iterable = "boo";
for (let value of iterable) {
  console.log(value);
}
// "b"
// "o"
// "o"

迭代Map

let iterable = new Map([["a", 1], ["b", 2], ["c", 3]]);

for (let entry of iterable) {
  console.log(entry);
}
// ["a", 1]
// ["b", 2]
// ["c", 3]

for (let [key, value] of iterable) {
  console.log(value);
}
// 1
// 2
// 3

 迭代Set

let iterable = new Set([1, 1, 2, 2, 3, 3]);

for (let value of iterable) {
  console.log(value);
}
// 1
// 2
// 3

對於for...of的循環,可以由breakthrow 或return終止。在這些情況下,迭代器關閉,

function* foo(){ 
  yield 1; 
  yield 2; 
  yield 3; 
}; 

for (let o of foo()) { 
  console.log(o); 
  break; // closes iterator, triggers return
}

 

for...of與for...in的區別 

無論是for...in還是for...of語句都是迭代一些東西。它們之間的主要區別在於它們的迭代方式。

for...in 語句以原始插入順序迭代對象的可枚舉屬性。

for...of 語句遍歷可迭代對象定義要迭代的數據。

以下示例顯示了與Array一起使用時,for...of循環和for...in循環之間的區別。

Object.prototype.objCustom = function() {}; 
Array.prototype.arrCustom = function() {};


// 每個對象將繼承objCustom屬性,並且作爲Array的每個對象將繼承arrCustom屬性,
// 因爲將這些屬性添加到Object.prototype和Array.prototype。
// 由於繼承和原型鏈,對象iterable繼承屬性objCustom和arrCustom。
let iterable = [3, 5, 7];
iterable.foo = 'hello world';


// 此循環僅以原始插入順序記錄iterable對象的可枚舉屬性。
// 它不記錄數組元素3, 5, 7 或hello,因爲這些不是枚舉屬性。 
// 但是它記錄了數組索引以及arrCustom和objCustom。
// 前面說了,不建議使用for...in迭代數組,這裏是純粹舉例才這樣寫,請勿模仿
for (let i in iterable) {
  console.log(i); // 0, 1, 2, "foo", "arrCustom", "objCustom"
}


// 這個循環類似於第一個,但是它使用hasOwnProperty() 來檢查,
// 如果找到的枚舉屬性是對象自己的(不是繼承的)。如果是,該屬性被記錄。
// 記錄的屬性是0, 1, 2和foo,因爲它們是自身的屬性(不是繼承的)。
// 屬性arrCustom和objCustom不會被記錄,因爲它們是繼承的。
for (let i in iterable) {
  if (iterable.hasOwnProperty(i)) {
    console.log(i); // 0, 1, 2, "foo"
  }
}


// 該循環迭代並記錄iterable作爲可迭代對象定義的迭代值,這些是數組元素 3, 5, 7,而不是任何對象的屬性。
for (let i of iterable) {
  console.log(i); // 3, 5, 7
}

在上面可以粗略看到,for...in循環的是對象的鍵(key),而for...of則是對象的值。

除此之外,for...of 不能循環非iterable對象。

let newObj = {a: '1', b: '2', c: '3', d: '4'};
for (let o of newObj) {
    console.log(o);    // Uncaught TypeError: newObj is not iterable
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章