例項解析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
的迴圈,可以由break
, throw
或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 }