ES6詳解七:迴圈的祕密 - iterator 和 yield

lihongxun945發表於2015-10-07

如果學過設計模式或者java之類的肯定知道 iterator 是什麼,在 Symbol.iterator 出現後,JS中也可以自己定義一個迭代器。
只要一個物件實現了正確的 Symbol.iterator 方法,那麼它就可以被 for in 所遍歷,如下所示:

var students = {}

students[Symbol.iterator] = function() {
  let index = 1;
  return {
    next() {
      return {done: index>100, value: index++}
    }
  }
}

for(var i of students) {
  console.log(i);
}

仔細看上面的程式碼就會明白迭代器是如何工作的。當執行 for(var i of students) 的時候,其實是呼叫了 students[Symbol.iterator]() 方法,這個方法返回了一個iterator(迭代器)。迭代器有一個next方法,for迴圈會不斷呼叫這個 iterator.next方法來獲取下一個值,直到返回值中的 done 屬性為true的時候結束迴圈。

那麼知道原理之後,我們可以自己來呼叫iterator.next來實現迴圈:

var students = {}

students[Symbol.iterator] = function() {
  let index = 1;
  return {
    next() {
      return {done: index>100, value: index++}
    }
  }
}

var iterator = students[Symbol.iterator]();

var s=iterator.next();
while(!s.done) {
  console.log(s.value);
  s=iterator.next();
}

上例中使用 iterator.next 和 while 結合實現了 for迴圈。

除了使用iterator 之外,我們還可以使用 yield 語法來實現迴圈,yield相對簡單一些,只要通過 yield 語句把值返回即可:

let students = {
  [Symbol.iterator]: function*() {
    for(var i=0;i<=100;i++) {
      yield i;
    }
  }
}

for(var s of students) {
  console.log(s);
}

看到這裡可能大家會懷疑 yield 和 iterator 到底是什麼關係。這個我還不能確定,不過從用法上來說,可以基本認為 yield 只是 iterator 的語法糖,它其實就是最終生成了一個 iterator
,下面我們通過取出 yield 生成的 iterator 就可以看出來:

let students = {
  [Symbol.iterator]: function*() {
    for(var i=0;i<=100;i++) {
      yield i;
    }
  }
}
var iterator = students[Symbol.iterator]();
var s=iterator.next();
while(!s.done) {
  console.log(s.value);
  s=iterator.next();
}

如上程式碼所示, yield 語句其實就是生成了一個 iterator ,完全和直接寫 iterator 沒區別,不過好處是使程式碼變得簡潔明瞭,因此建議直接使用 yield 語法而不要費力的去寫 iterator。

看完 上面的iterator 和 yield語法,其實看似神祕的迴圈也就很容易理解了。

相關文章