yield*語句

如果在一個Generator函數內部調用另一個Generator函數,默認情況下是沒有效果的。

function *gen1() {
yield 3;
yield 4;
}

function *gen2() {
yield 1;
yield 2;
gen1();
yield 5;
}

for (let v of gen2()) {
console.log(v);
}

/*
1
2
5
*/

如果要想期望中輸出1到5,就必須使用yield*語句,具體用如下:

function *gen1() {
yield 3;
yield 4;
}

function *gen2() {
yield 1;
yield 2;
yield *gen1();
yield 5;
}

for (let v of gen2()) {
console.log(v);
}
/*
1
2
3
4
5
*/

如果不加星號,那麼在執行到yield gen1()的時候,返回的是一個遍歷器對象。而加了星號,則會進入遍歷器對象,執行內部的yield語句。

yield*語句等同於在Generator內部部署了一個for...of循環。

function *gen1() {
yield 3;
return 4; // 注意這裡改成了return,而不是yield
}

function *gen2() {
yield 1;
yield 2;
yield *gen1();
yield 5;
}

var g = gen2();
for (let v of gen2()) {
console.log(v);
}
/*
1
2
3
5
*/

如果yield*後面緊跟一個數組,由於數組原生支持遍歷器,因此會遍曆數組成員。

任何數據結構只要具有Iterator介面,就可以用yield*遍歷。

yield*語句可以很方便的取出嵌套數組的所有成員。

const array = [1,2,3,[4,5,[6,7]],8];
function *output(arr) {
if (Array.isArray(arr)) {
for (let i = 0; i < arr.length; i++) {
yield* output(arr[i]); // output是一個Generator函數,yield必須加星號
}
} else {
yield arr;
}
}

for (let v of output(array)) {
console.log(v);
}
/*
1
2
3
4
5
6
7
8
*/

作為對象屬性的Generator函數

如果一個對象的屬性是Generator函數,可以建寫成如下形式:

let obj = {
*method() {}
};
// 等價於
let obj = {
method: function *() {}
};

Generator函數的this

Generator函數總是返回一個遍歷器,ES6規定這個遍歷器是Generator函數的實例,它也繼承了Generator函數的prototype對象上的方法。

function *gen() {}
var g = gen();
g instanceof gen; // true
Object.getPrototypeOf(g) === gen.prototype; // true

如果把Generator函數當作構造函數調用,會拋出TypeError異常。

如果要把Generator中的this綁定到對象上,可以使用bind方法綁定Generator函數內部的this,再正常調用Generator函數即可。

function *gen() {
yield this.x = 1;
yield this.y = 2;
}

var obj = {};
var g = gen.bind(obj)();
g.next();
obj; // { x: 1 }
g.next();
obj; // { x:1, y:2 }

Generator函數推導

ES7在數組推導的基礎上提出了Generator函數推導。for...of循環會自動調用遍歷器的next方法,將其返回值的value屬性作為數組的一個成員。

Generator函數推導是對數組結構的一種模擬,其最大的優點是惰性求值,即直到用到時才會求值,這樣可以保證效率。

let bigArray = new Array(100000);
for (let i = 0; j = bigArray.length; i < j; i++) {
bigArray[i] = i;
}

let first = bigArray.map(n => n * n)[0];
first; // 0

上述代碼遍歷一個大數組,在真正遍歷之前,數組已經生成,佔用了大量的系統資源。如果採用Generator函數推導,可以避免這一點。

let bigGen = function *() {
for (let i = 0; i < 100000; i++) {
yield i;
}
}

let g = ( for (n of bigGen()) { n * n });
g.next().value; // 0

上述代碼僅在支持ES7的環境下能正常編譯。


推薦閱讀:
查看原文 >>
相關文章