簡介
迭代器是一種設計模式,可在容器物件 如 連結串列、陣列上遍歷,無需關心容器物件的記憶體分配的實現細節。簡單的理解就是可以一個一個的依次拿到其中的資料,類似一個移動的指標,但是會告訴我們什麼時候結束。這樣我們可以拿到資料之後可以做一些我們需要做的事情。
js 中的迭代器是什麼樣子的
在javascript 中迭代器是一個特殊物件,這個迭代器物件有一個next()方法,每次呼叫都返回一個物件(結果物件)。結果物件有兩個屬性:一個是value,表示下一個將要返回的值;另一個是done,它是一個布林型別的值,如果已經迭代到序列中的最後一個值,則它為 true。迭代器還會儲存一個內部指標,用來指向當前集合中值的位置,每呼叫一次next()方法,都會返回下一個可用的值,類似下面這個物件的結構。
{ next: function () { return { value:'', done: true / false } } }
迭代協議
隨著javascript 語言的能力進一步提升,新增了一些新的資料型別 如 Map、Set、WeakMap 等,為了這些不同的資料結構,可以統一的迭代,es6 增加了迭代協議這個東西。
迭代協議並不是新的內建實現或語法,而是協議。這些協議可以被任何遵循某些約定的物件來實現。
迭代協議具體分為兩個協議:可迭代協議和迭代器協議。
簡單的理解就是在js 中任何物件只要滿足迭代協議就可以遍歷
可迭代協議
要成為可迭代物件, 一個物件必須實現 @@iterator 方法。這意味著物件(或者它原型鏈上的某個物件)必須有一個鍵為 @@iterator 的屬性,可通過常量 Symbol.iterator 訪問該屬性:
簡單的理解,你想讓一個東西可以遍歷,那麼這個東西要有一個 @@iterator ,這個屬性可以通過Symbol.iterator 訪問
屬性 |
值 |
[Symbol.iterator] |
一個無引數的函式,其返回值為一個符合迭代器協議的物件。 |
迭代器協議
迭代器協議定義了產生一系列值(無論是有限個還是無限個)的標準方式。當值為有限個時,所有的值都被迭代完畢後,則會返回一個預設返回值。
只有實現了一個擁有以下語義(semantic)的 next() 方法,一個物件才符合迭代器協議:
屬性 |
值 |
next |
一個無引數函式,返回一個應當擁有以下兩個屬性的物件: done(boolean) next() 方法必須返回一個物件,該物件應當有兩個屬性: done 和 value,如果返回了一個非物件值(比如 false 或 undefined),則會丟擲一個 異常("iterator.next() returned a non-object value")。 |
迭代過程
當一個物件需要被迭代的時候(比如被寫入一個 for...of 迴圈時),首先,會不帶引數呼叫它的 @@iterator 方法( 此時返回的是結構是這樣的 { next: function () {}}),然後使用此方法返回的迭代器獲得要迭代的值(其實就是不斷的呼叫這個next()方法)
迭代總結
迭代協議可以總結為,一個東西要遍歷,必須滿足可迭代協議跟迭代器協議
- 可迭代協議:這個物件必須有@@iterator,可以通過Symbol.iterator 訪問
- 迭代器協議:是一個物件,這個物件的next() 函式返回一個物件,這個物件包括兩個屬性,一個是value,一個是done(boolean,是否是最後一個元素,done 為 true 時 value 可省略)
也就是說 迭代器物件本質上,就是一個指標物件。通過指標物件的next(),用來移動指標。
自定義迭代
物件是沒有實現迭代器,所以不能遍歷物件,為了可以實現物件的遍歷,我們需要在物件上實現上面說的迭代器,通常有兩種寫法,一種是傳統的寫法,這種需要自己去控制內部的狀態,另外一種是利用生成器函式返回的Generator的迭代器來實現,程式碼如下:
傳統寫法
let obj = { name: 'joel', adress: 'gz', [Symbol.iterator]: () => { // 這裡不要用this, 因為是return fn, this 會丟失 let index = -1, atrrList = Object.keys(obj); const objIterator = { next: () => { let result = '' index++ if (index < atrrList.length) { result = { value: atrrList[index], done: false } } else { result = { done: true } } return result } } return objIterator } } for (const item of obj) { console.log('atrrs:' + item + ',value:' + obj[item]) }
生成器函式寫法
// 為不可迭代的物件新增迭代器 let obj = { a: 1, b: 2 } obj[Symbol.iterator] = function* () { let keys = Object.keys(obj); //取到key值的長度 let len = keys.length; //定義迴圈變數 let n = 0; //條件判斷 while (n <= len - 1) { yield { k: keys[n], v: obj[keys[n]] }; n++ } } //返回的是個物件的key和value for (let { k, v } of obj) { console.log(k, v); }
其他相關如內建可迭代物件、用於可迭代物件的語法、接受可迭代物件的內建api 等 請點選 這裡