參考Babel的learn-es2015和 MDN- iteration protocols的解釋。
ES6定義iteratable protocol
和 iterator protocol
兩個協定。
iterable
protocol 允許 JavaScript物件定義或是客製化他們自己的迭代行為(iteration behavior)。例如使用for..of
結構來loop所有的值。
像 Array
或 Map
已經有預設的iteration behavior, 有的沒有(例如Object
)
為了要可以iterable, 物件必須要實作 iterator method, 就是說該物件要有一個屬性有Symbol.iterator
key, 其值為一個不帶參數的函式, 這個函式傳回一個符合 iterator protocol
的物件。
當這個物件需要被遍歷(iteratored), 例如使用for..of
迴圈, 其 iterator method就會被呼叫傳回iterator
, 我們就可以利用這個iterator來loop取值。
iterator
protocol 定義一個標準的方法來產生一個值的序列(無限或有限)。
一個物件只要實作了next()
method, 就被當作是一個iterator。next()
是一個無參數的函式, 傳回帶有兩個以下兩個屬性的物件:
例如String
就有內建的iterable物件了, 預設的iteraor會一個一個傳回字串的字元:
'use strict';
var s = 'hi';
var it = s[Symbol.iterator]();
console.log(it + ''); // [object String Iterator]
console.log(it.next()); // { value: "h", done: false }
console.log(it.next()); // { value: "i", done: false }
console.log(it.next()); // { value: undefined, done: true }
定義我們自己的iterator:
'use strict';
function makeIterator(array){
var nextIndex = 0;
return {
next: function(){
return nextIndex < array.length?
{value: array[nextIndex++], done: false}:
{done: true};
}
};
}
var it = makeIterator(['yo', 'ya']);
console.log(it.next().value); // yo
console.log(it.next().value) // ya;
console.log(it.next().done); // true
for...of
和for...in
loop的差別在於, for...in
是遍歷(iterate)屬性名(property names), 那for...of
是遍歷屬性值(property values):
let arr = [3, 5, 7];
arr.foo = "hello";
for (let i in arr) {
console.log(i); // logs "0", "1", "2", "foo"
}
for (let i of arr) {
console.log(i); // logs "3", "5", "7"
}
babel的範例:
let fibonacci = {
[Symbol.iterator]() {
let pre = 0, cur = 1;
return {
next() {
[pre, cur] = [cur, pre + cur];
return { done: false, value: cur }
}
}
}
}
for (var n of fibonacci) {
// truncate the sequence at 1000
if (n > 1000)
break;
console.log(n);
}
function
宣告前面加上*
號, 就表示我們定義了一個genrator function
. 這個函式會傳回一個Generator
物件。
function * name([param[, param[, ... param]]]) {
//statements
}
Generators are functions which can be exited and later re-entered. Their context (variable bindings) will be saved across re-entrances.
呼叫generator function不會立刻執行,會先回傳一個iterator
物件, 當iterator物件的next()
method被呼叫, 這個函式的主體就會開始執行,直到遇到第一個yield
運算式為止。
yield
會從iterator指定一個值回傳, 或是連同yield*
發送值給其他的generator function。
接下來next()
method傳回一個物件, 這個物件帶有一個包含被送出(yielded)的值的屬性,以及一個done
屬性指出是否這個generator已經發送出其最新的值。
例如:
function* idMaker(){
var index = 0;
while(index < 3)
yield index++;
}
var gen = idMaker();
console.log(gen.next()); // { value: 0, done: false }
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: undfined, done: true }
使用 yield*
呼叫其他的generator function:
function* anotherGenerator(i) {
yield i + 1;
yield i + 2;
yield i + 3;
}
function* generator(i){
yield i;
yield* anotherGenerator(i);
yield i + 10;
}
var gen = generator(10);
console.log(gen.next().value); // 10
console.log(gen.next().value); // 11
console.log(gen.next().value); // 12
console.log(gen.next().value); // 13
console.log(gen.next().value); // 20
babel範例, 改寫:
'use strict';
require("babel/polyfill");
var fibonacci = {
[Symbol.iterator]: function*() {
var pre = 0, cur = 1;
for (;;) {
var temp = pre;
pre = cur;
cur += temp;
yield cur;
}
}
}
for (var n of fibonacci) {
// truncate the sequence at 1000
if (n > 1000)
break;
console.log(n);
}
iteration Iteration_protocols -- MDN