迭代器及迭代器協議 - 拾起忽略的知識點

迭代器是咱們咱們平時接觸不少的一個特性es6

當咱們遍歷數組、字符串或者使用解構賦值、對象展開運算符時都會用到迭代器協議相關內容數組

1. 什麼是可迭代協議和迭代器協議

①可迭代協議

顧名思義,就是讓對象支持被迭代的協議promise

當咱們執行如下代碼時,能夠明確知道什麼值能夠被循環獲得異步

let a = [1,2,3]
for(let i of a) {
 console.log(i) // 1,2,3
}
複製代碼

如何支持可迭代協議呢?
對象(或者它原型鏈上的某個對象)必須有一個名字是 Symbol.iterator 的屬性async

Symbol.iterator : 提供一個無參函數, 返回的對象實現迭代器協議函數

② 迭代器協議

迭代器協議定義瞭如何產生和返回迭代器函數的結果測試

實現迭代器協議的對象至少應該包含一個next方法ui

next方法:
提供一個對象的無參函數,返回的對象擁有兩個屬性:
done: boolean 不提供則默認是false
value: 用於迭代時獲取的對象this

2. 驗證現有實現可迭代協議對象是否具有上述的屬性

驗證迭代對象中的next方法spa

let a = [1,2,3]
var aIt = a[Symbol.iterator]()
aIt.next()
//{value: 1, done: false}
aIt.next()
//{value: 2, done: false}
aIt.next()
//{value: 3, done: false}
aIt.next()
//{value: undefined, done: true}
複製代碼

next方法用於依次返回用於迭代的對象,咋一看,這不就是 generator的next方法嗎

3. 如何對自定義對象實現迭代器協議

a. 經過擴展對象,實現協議定義的方法和對象

var c={a:1,1:2,c:4,b:5}
for(let i of c) {
 console.log(c)
}
//Uncaught TypeError: c is not iterable at <anonymous>:2:14
複製代碼

接下里咱們按照上述協議內容將c對象擴展成支持for...of遍歷, 首先實現可迭代協議 即爲對象添加Symbol.iterator屬性

c[Symbol.iterator] = function(){
  return {}
}
複製代碼

如何實現迭代器協議? 即在return中返回包含next方法的對象

c[Symbol.iterator] = function(){
  return {
    _i:0,
    next() {
       let keys = Object.keys(c)
        if(this._i<keys.length){
          console.log("調用iterator-next:"+i)
          return {value: keys[this._i++],done: false}
        }else {
         return {done: true, value:undefined}  
      }
    }
  }
}
複製代碼

測試調用

for(let i of c){console.log(i)}
//1 a c b
複製代碼

注: 以上實現中 next方法 this對象指向的是next返回對象

b. 利用generator函數和對象

var c={a:1,1:2,c:4,b:5}
function* ci() {
   let keys = Object.keys(c),i=0
   while(i<keys.length){
    yield keys[i++]
  } 
}
c[Symbol.iterator] = ci 
複製代碼

調用測試

for(let i of c){console.log(i)}
// 1 a c b
複製代碼

爲何能夠使用generator實現
1.(MDN)調用一個生成器函數並不會立刻執行它裏面的語句,而是返回一個這個生成器的 迭代器 (iterator )對象。當這個迭代器的 next() 方法被首次(後續)調用時,其內的語句會執行到第一個(後續)出現yield的位置爲止,yield 後緊跟迭代器要返回的值。或者若是用的是 yield*(多了個星號),則表示將執行權移交給另外一個生成器函數(當前生成器暫停執行)
2. cig = new ci()// generator對象。 cig[Symbol.iterator]().next === cig.next

c. 在對象原型鏈上實現通用的迭代器協議

Object.prototype[Symbol.iterator] = function(){
    var i=0;
    return {next:() =>{
      var keys = Object.keys(this)
      if(i< keys.length){
        return {value: keys[i++],done:false}
      }else{ 
       return {done: true} 
      }
      }
    }
}
複製代碼

測試

var a = {b:1}
for(let i of a){console.log(i)}
// b
for(let i in a){console.log(i)}
// b
複製代碼

3. 實現可迭代協議有什麼做用

①. es6中的for...of語法就是基於迭代協議的

let a = [1,2,3]
for(let i of a) {
  console.log(i)
}
//1
//2
//3
複製代碼

②. 對象展開符

var b = {a:1,1:2,c:4,b:5}
[...b]
// Uncaught TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))
複製代碼

而對上面咱們修改的對象C執行如下代碼

[...c]
// ["1", "a", "c", "b"]
複製代碼

③. yield* 表達式

function* a(){
 yield* c
}
var ac = a()
ac.next()
//{value: "1", done: false}
ac.next()
//{value: "a", done: false}
ac.next()
//{value: "c", done: false}
ac.next()
//{value: "b", done: false}
ac.next()
//{value: undefined, done: true}
複製代碼

④. 解構賦值

藉助對象C的console語句觀察解構賦值的過程

[a1,b1] = c
// 調用iterator-next:0
// 調用iterator-next:1
// a1: 1 b1: a
複製代碼

以上就是迭代協議相關內容及運用

發現了Symbol.iterator屬性的神奇之處,忍不住又看看了Symbol其餘的數據, 發現又一個叫asyncIterator的屬性

參考iterator, 那就是定義了一個異步可迭代對象了吧 ~_~

4. asyncIterator屬性做用

用於被for await...of 語句循環使用

(注: for await...of 可用於遍歷同步或異步實現迭代器協議的對象)

①. 實現方式

同iterator,須要含義Symbol.asyncIterator屬性, 提供返回一個無參函數,返回一個包含next方法的對象

②. 舉個栗子

暫時可能沒有想到什麼必須合適的場景,那就無病呻吟一下吧,↓

先驗證下for await...of

var a = [Promise.resove(1),Promise.resolve(2),Promise.resolve(3)]
 
for(let i of a){
 console.log(i)
}
// promise * 3
 
for await(let i of a) {
  console.log(i)
}
// 1,2,3
複製代碼

構造一個異步可迭代對象

var asyncIterable = {
  [Symbol.asyncIterator]() {
    return {
      i: 0,
      next() {
        if (this.i < 3) {
          return Promise.resolve({ value: this.i++, done: false });
        }
 
        return Promise.resolve({ done: true });
      }
    };
  }
};
 
for await (let num of asyncIterable) {
  console.log(num);
}
// 0,1,2
複製代碼

③,現狀

目前還木有實現了asyncIterator內置屬性的對象

5. Iterator對象

注: 非標準屬性,只在FF下有實現,慎用

Iterator 函數返回一個對象,它實現了遺留的迭代協議,而且迭代了一個對象的可枚舉屬性。

var a = {a: 1,1: 2,c:3,b:4};
 
for (var [name, value] of Iterator(a)) {
  console.log(name, value); 
}
// 1,2 a,1 c,3 b,4
複製代碼
相關文章
相關標籤/搜索