咱們暫且以數組爲例,javascript提供了多種遍歷數組的方法,最開始咱們可能習慣使用for循環:javascript
for (var index = 0; index < myArray.length; index++){ console.log(myArray[index]); }
這種寫法比較麻煩,因而數組內提供了forEach方法:java
myArray.forEach(function (value) { console.log(value); });
這段代碼看起來更加簡潔,但這種方法也有一個小缺陷:你不能使用break語句中斷循環,也不能使用return語句返回到外層函數。
for...in循環(其實for...in循環是爲了遍歷對象而設計的,並不適合遍歷數組,切記!切記!切記!重要的事情說三遍!!!)es6
for (var index in myArray) { // 千萬別這樣作 console.log(myArray[index]); }
for...in循環便利數組絕對是一個糟糕的選擇,緣由以下:數組
在這段代碼中,賦給index的值不是實際的數字,而是字符串「0」、「1」、「2」,此時極可能在無心之間進行字符串算數計算,例如:「2」 + 1 == 「21」,這給編碼過程帶來極大的不便。數據結構
做用於數組的for-in循環體除了遍歷數組元素外,還會遍歷自定義屬性。舉個例子,若是你的數組中有一個可枚舉屬性myArray.name,循環將額外執行一次,遍歷到名爲「name」的索引。就連數組原型鏈上的屬性都能被訪問到。ide
最讓人震驚的是,在某些狀況下,這段代碼可能按照隨機順序遍歷數組元素。函數
簡而言之,for-in是爲普通對象設計的,你能夠遍歷獲得字符串類型的鍵,所以不適用於數組遍歷。this
強大的for-of循環編碼
for (var value of myArray) { console.log(value); }
優勢:spa
其實不只能夠便利數組,ES6新增長的數據結構set和map也可使用for-of循環來進行遍歷
var engines = new Set(["Gecko", "Trident", "Webkit", "Webkit"]); for (var e of engines) { console.log(e); } // Gecko // Trident // Webkit var es6 = new Map(); es6.set("edition", 6); es6.set("committee", "TC39"); es6.set("standard", "ECMA-262"); for (var [name, value] of es6) { console.log(name + ": " + value); } // edition: 6 // committee: TC39 // standard: ECMA-262
上面代碼演示瞭如何遍歷 Set 結構和 Map 結構。值得注意的地方有兩個,首先,遍歷的順序是按照各個成員被添加進數據結構的順序。其次,Set 結構遍歷時,返回的是一個值,而 Map 結構遍歷時,返回的是一個數組,該數組的兩個成員分別爲當前 Map 成員的鍵名和鍵值。
甚至使用for-of循環亦能夠遍歷字符串:
for (var chr of "") { alert(chr); }
遍歷器(Iterator)就是這樣一種機制。它是一種接口,爲各類不一樣的數據結構提供統一的訪問機制。任何數據結構只要部署Iterator接口,就能夠完成遍歷操做(即依次處理該數據結構的全部成員)。
Iterator的做用有三個:一是爲各類數據結構,提供一個統一的、簡便的訪問接口;二是使得數據結構的成員可以按某種次序排列;三是ES6創造了一種新的遍歷命令for...of循環,Iterator接口主要供for...of消費。
Iterator的遍歷過程是這樣的。
(1)建立一個指針對象,指向當前數據結構的起始位置。也就是說,遍歷器對象本質上,就是一個指針對象。
(2)第一次調用指針對象的next方法,能夠將指針指向數據結構的第一個成員。
(3)第二次調用指針對象的next方法,指針就指向數據結構的第二個成員。
(4)不斷調用指針對象的next方法,直到它指向數據結構的結束位置。
每一次調用next方法,都會返回數據結構的當前成員的信息。具體來講,就是返回一個包含value和done兩個屬性的對象。其中,value屬性是當前成員的值,done屬性是一個布爾值,表示遍歷是否結束。
在ES6中,有些數據結構原生具有Iterator接口(好比數組),即不用任何處理,就能夠被for...of循環遍歷,有些就不行(好比對象)。緣由在於,這些數據結構原生部署了Symbol.iterator屬性,另一些數據結構沒有。凡是部署了Symbol.iterator屬性的數據結構,就稱爲部署了遍歷器接口。調用這個接口,就會返回一個遍歷器對象。
在ES6中,有三類數據結構原生具有Iterator接口:數組、某些相似數組的對象、Set和Map結構。對於這三類數據結構,不用本身寫遍歷器生成函數,for...of循環會自動遍歷它們。除此以外,其餘數據結構(主要是對象)的Iterator接口,都須要本身在Symbol.iterator屬性上面部署,這樣纔會被for...of循環遍歷。
有些數據結構是在現有數據結構的基礎上,計算生成的。好比,ES6的數組、Set、Map 都部署瞭如下三個方法,調用後都返回遍歷器對象。
這三個方法調用後生成的遍歷器對象,所遍歷的都是計算生成的數據結構。
屏幕快照 2017-06-07 下午3.46.23.png
值得注意的是ES6中的新數據結構set和map原生提供的除了這三個遍歷器生成函數以外,還有一個遍歷方法forEach()。切記不要將遍歷器生成函數和遍歷方法混爲一談
let map = new Map([ ['f','no'], ['t','true'] ]); for (let[key,value] of map.entries()){ console.log(key,value); } //等同於使用map.entries() for (let[key,value] of map){ console.log(key,value); }
前面咱們提到過那些沒有部署過Symbol.iterator屬性的數據結構也便是不具有Iterator接口的的對象是不能使用for...of方法遍歷的。經過Gnenrator函數爲這類對象加上這個Iterator接口就能夠了。
function* objectEntries(obj) { let propKeys = Reflect.ownKeys(obj); for (let propKey of propKeys) { yield [propKey, obj[propKey]]; } } let jane = { first: 'Jane', last: 'Doe' }; for (let [key, value] of objectEntries(jane)) { console.log(`${key}: ${value}`); } // first: Jane // last: Doe
上面代碼中,對象jane原生不具有 Iterator 接口,沒法用for...of遍歷。這時,咱們經過 Generator 函數objectEntries爲它加上遍歷器接口,就能夠用for...of遍歷了。加上遍歷器接口的另外一種寫法是,將 Generator 函數加到對象的Symbol.iterator屬性上面。
function* objectEntries() { let propKeys = Object.keys(this); for (let propKey of propKeys) { yield [propKey, this[propKey]]; } } let jane = { first: 'Jane', last: 'Doe' }; jane[Symbol.iterator] = objectEntries; for (let [key, value] of jane) { console.log(`${key}: ${value}`); } // first: Jane // last: Doe
除了for...of循環之外,擴展運算符(...)、解構賦值和Array.from方法內部調用的,都是遍歷器接口。這意味着,它們均可以將 Generator 函數返回的 Iterator 對象,做爲參數。
function* numbers () { yield 1 yield 2 return 3 yield 4 } // 擴展運算符 [...numbers()] // [1, 2] // Array.from 方法 Array.from(numbers()) // [1, 2] // 解構賦值 let [x, y] = numbers(); x // 1 y // 2 // for...of 循環 for (let n of numbers()) { console.log(n) } // 1 // 2
總而言之,ES6提供一個強大的遍歷方法for-of,對於那些原生具有Iterator接口的數據結構,能夠直接使用,而不具有具有Iterator接口的對象,須要部署Symbol.iterator屬性才能使用。
做者:大闖仔 連接:https://www.jianshu.com/p/b9e31305bcbb 來源:簡書 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。