iterators & generators-《understanding ECMAScipt6》讀書筆記8

weixin_34320159發表於2017-05-31

前言

果然不能立flag,上次文章都寫完了,可是hexo出了點小問題,再加上正好回家,所以延後了幾天。

發現之前一直提到 迭代器 和 生成器的字眼。其實一直沒有講這方面的東東。iterators 在es6中應用的還是挺廣的,set,map,array,string都有這樣的入口,generators是es7中async的基礎。個人認為還是挺重要的。

iterators

很多語言都有iterators的概念。
在ES6中:
for-of迴圈基於iterators
...(擴散操作符)基於iterators
甚至非同步程式也用到iterators

為什麼需要iterators

迴圈存在的問題

需要多個變數去標記它,通用的比如i,len;
(其實這點我個人還不是很能理解)

iterators是什麼

iterators是一個擁有next()方法的物件,每次呼叫next()方法,返回一個下面的結構

{
  done: false, //表示是否到達迴圈的終點
  value: //該項的值
}

ES6中內建的iterators

集合物件

ES6 有三個型別的集合物件

  • Arrays
  • Maps
  • Sets

他們都有以下的方法

  • entries(),返回一個[key,value]的iterators
    • Arrays返回的key為數字序號
    • Sets,返回的key和value一樣的值
    • Maps,返回的key為正常的key
  • keys(),返回key的iterators
    • 跟entries()中返回key是一樣的
  • values(),返回一個values的iterators
    • 跟entries()中返回value是一樣的

如果不指定迭代器方法的話,
Arrays和Sets呼叫的是values(),Maps呼叫的是entries()

判斷一個物件是不是迭代器 typeoof Object[Symbol.iterator] === 'function'

String迭代器

ES5中string存在效率低,無法識別雙位元組字元的情況。
ES6中解決了無法識別雙位元組字元的bug

NodeList迭代器

在ES5 中DOM的Nodelist和arrays區別很容易讓人困惑
ES6為DOM的Nodelist也增加了一個預設的迭代器,同陣列一樣,所以也可以使用for-of去迴圈

...操作符

...操作符是從迭代器中讀取資料並依次插入的

generators

什麼是generators

  • generators是一個返回值是iterators的函式
  • generators在function關鍵字後有號並且內部有yield關鍵字,號不一定要緊跟在function關鍵字後面
  • 生成器執行時會在每次yield後停止執行,注:yield關鍵字只能存在於generators內部
  • 書寫在物件內部的方法
let o1 = {
  createIterator:function*(){

  }
}

let o2 = {
  *createIterator(){

  }
}
//這兩種都可以

generator 傳參

往next()內傳參的話,會替換掉上次yield的返回值。

function *createIterator(){
  let first = yield 1;
  let second = yield first + 2;
  yield second + 3;
}
let iterator = createIterator();

iterator.next() //{value:1,done:false}
iterator.next(4) //{value:6,done:false}
iterator.next(5)//{value:8,done:true}

generator 拋異常

iterator.throw()

  • 在預計丟擲異常的時候,可以用try..catch捕獲
  • 如果異常被捕獲,則throw()相當於next()返回的結果
  • 如果generator內有return ,則return 後面的程式碼均不會執行

generator內可巢狀generator

非同步任務執行

利用generator的特性執行多個非同步任務,後續可以用Promise()進行改造

普通任務

該任務不需要利用上一步任務的返回值。

function run (tasks){
  let task = tasks();
  let result = task.next();

  function step (){
    if(!result.done){
      result = task.next();
      step();
    }
  }

  step();
}


run(function * (){
  console.log(1);
  yield;
  console.log(2);
  yield;
  console.log(3);
})

帶資料的任務

其實只變動了一處,將資料放在next()函式內部

function run (tasks){
  let task = tasks();
  let result = task.next();

  function step (){
    if(!result.done){
      result = task.next(result.value);
      step();
    }
  }

  step();
}

非同步執行任務

如果result.value是一個函式

//假想的函式
function readFile(filename){
  return function(cb){
    fs.readFile(filename,cb);
  };
}

function run (tasks){
  let task = tasks();
  let result = task.next();

  function step(){
    if(!result.done){
      if(typeof result.value === 'function'){
        result.value(function(err,data){
          if(err){
            task.throw(err);
          }else{
            task.next(data);
            step();
          }
        })
      }

    }
  }

  step();
}

從上文程式碼中可以看的出來缺點:

  • 必須要求函式為err優先
  • 兩個函式的引數傳遞依賴於next()

相關文章