在開發過程當中常常須要循環遍歷數組或者對象,js也爲咱們提供了很多方法供使用,其中就有三兄弟forEach、for...in、for...of,這三個方法應該是使用頻率最高的,但不少人卻一值傻傻分不清,常常該混淆了它們的功能和注意點。就在今天,我來給它們一個大區分(*・ω< )。數組
forEach() 方法對數組的每一個元素執行一次提供的函數。函數
從ES5開始,Javascript內置了forEach方法,用來遍歷數組 。spa
var arr = ['a', 'b', 'c', 'd', 'e']; arr.forEach(function(item) { console.log(item); // a,b,c,d,e });
!注意:forEach方法沒辦法使用
break
語句跳出循環,或者使用return
從函數體內返回。prototype
for...in語句以任意順序遍歷一個對象的可枚舉屬性。對於每一個不一樣的屬性,語句都會被執行。設計
語法:code
for (variable in object) {...}variable 在每次迭代時,將不一樣的屬性名分配給變量。對象
object 被迭代枚舉其屬性的對象。blog
// 例子一 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 語句在可迭代對象(包括 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...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 }