javascript 之迭代器

簡介

  迭代器是一種設計模式,可在容器對象 如 鏈表、數組上遍歷,無需關心容器對象的內存分配的實現細節。簡單的理解就是可以一個一個的依次拿到其中的數據,類似一個移動的指針,但是會告訴我們什麼時候結束。這樣我們可以拿到數據之後可以做一些我們需要做的事情。

 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 等 請點擊 這裏

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章