Generator函數,能夠理解爲一種狀態機,封裝了多個內部狀態。是一個遍歷器生成器,返回遍歷器對象(即Generator 函數的內部指針)。bash
Generator函數和普通函數區別:函數
function* foo(){
console.log('hello');
}
var bar = foo();
bar.next();
bar.next();
//'hello'
複製代碼
一個普通函數中使用yield表達式,結果產生一個句法錯誤ui
(function(){
yield 1;
})(); // 報錯
(function*(){
yield 1;
})();//ok
複製代碼
function* foo() {
console.log('start');
yield 1;
console.log('middle');
yield 2;
console.log('end');
return 3;
}
var bar = foo();
console.log(bar.next());
console.log(bar.next());
console.log(bar.next());
console.log(bar.next());
//start
//{ value: 1, done: false }
//middle
//{ value: 2, done: false }
//end
//{ value: 3, done: true }
//{ value: undefined, done: true }
複製代碼
Generator函數的運行流程:this
區別在於每次遇到yield,函數暫停執行,下一次再從該位置繼續向後執行,而return語句不具有位置記憶的功能。spa
yield表達式自己沒有返回值,或者說老是返回undefined。yield表達式若是用在另外一個表達式之中,必須放在圓括號裏面指針
function* foo() {
//在另外一個表達式中,yield表達式必須加上圓括號
console.log( 'hello' + (yield 'world'));
}
var bar = foo();
console.log(bar.next());
console.log(bar.next());
//{ value: 'world', done: false }
//helloundefined
//{ value: undefined, done: true }
複製代碼
yield表達式用做函數參數或放在賦值表達式的右邊,能夠不加括號code
function fun(a){
console.log('a:'+ a);
}
function* foo() {
fun(yield 'hello', yield 'world'); //函數參數
let value = yield; //表達式右側
}
var bar = foo();
console.log(bar.next());
console.log(bar.next());
console.log(bar.next());
console.log(bar.next());
//{ value: 'hello', done: false }
//{ value: 'world', done: false }
//a:undefined
//{ value: undefined, done: false }
//{ value: undefined, done: true }
複製代碼
任意一個對象的Symbol.iterator方法等於該對象的遍歷器生成函數,調用該函數會返回該對象的一個遍歷器對象。對象
Generator 函數執行後,返回一個遍歷器對象。該對象自己也具備Symbol.iterator屬性,執行後返回自身。接口
function* foo() {
yield 1;
}
var bar = foo();
bar === bar[Symbol.iterator]() //true
複製代碼
Generator 函數賦值給Symbol.iterator屬性,從而使得myIterable對象具備了 Iterator 接口,能夠被...運算符遍歷了generator
var myIterable = {};
myIterable[Symbol.iterator] = function* foo() {
yield 1;
yield 2;
};
[...myIterable] //[1, 2]
複製代碼
next方法能夠帶一個參數,該參數就會被看成上一個yield表達式的返回值
function* foo() {
let a = 10;
let b = yield 10 + a;
yield b + 10;
}
var bar = foo();
bar.next(); //{ value: 20, done: false }
bar.next(20); //{ value: 30, done: false }
bar.next(); //{ value: undefined, done: true }
複製代碼
第二個next參數20做爲,第一次yield的返回值,全部b = 20 ,因此第二個yield表達式爲值爲30
Generator 函數從暫停狀態到恢復運行,它的上下文狀態(context)是不變的。經過next方法的參數,就有辦法在 Generator 函數開始運行以後,繼續向函數體內部注入值。
for...of循環遍歷Iterator對象,再也不須要調用next方法
function* foo() {
yield 1;
yield 2;
return 3;
}
for (let v of foo()) {
console.log(v);
}//1 2
複製代碼
須要注意,一旦next方法的返回對象的done屬性爲true,for...of循環就會停止,且不包含該返回對象,因此上面代碼的return語句返回的3,不包括在for...of循環之中
for...of循環、擴展運算符(...)、解構賦值和Array.from方法內部調用的,都是遍歷器接口。這意味着,它們均可以將 Generator 函數返回的 Iterator 對象,做爲參數。
使對象類型數據也能使用for...of 第一種
function* foo() {
let keys = Object.keys(this);
for (let key of keys) {
yield [key, this[key]];
}
}
var bar = { name: 'li', age: 20};
bar[Symbol.iterator] = foo;
for (let value of bar) {
console.log(value);
}
//[ 'name', 'li' ]
//[ 'age', 20 ]
複製代碼
第二種實現
function* foo(obj) {
let keys = Object.keys(obj);
for(let key of keys){
yield [key, obj[key]];
}
}
var bar = {name: 'li', age: 20, sex: 'man'};
for (let value of foo(bar)){
console.log(value);
}
複製代碼
generator.throw()拋出錯誤先看內部又沒有捕獲,若是內部沒有捕獲,就外部捕獲。若是都沒有捕獲,那麼程序將報錯,直接中斷執行。
注意區分throw方法和全局的throw
function* foo() {
try{
yield 1;
} catch(error){
console.log('error');
}
yield 2;
}
var bar = foo();
bar.next(); //{ value: 1, done: false }
bar.throw();//{ value: 2, done: false }
throw new Error('xxx');
複製代碼
若是return方法傳有參數就當作返回對象的value值。日後再調用next方法都返回{ value: undefined, done: true }
function* gen() {
yield 1;
yield 2;
yield 3;
}
var g = gen();
g.next(); //{ value: 1, done: false }
g.return('foo'); //{ value: 'foo', done: true }
g.next(); //{ value: undefined, done: true }
複製代碼
若是Generator中有try...finaly,調用return()方法後,就開始執行finally代碼塊,不執行try裏面剩下的代碼了,而後等到finally代碼塊執行完,再返回return()方法指定的返回
function* gen() {
try {
yield 1;
yield 2;
} finally {
yield 3;
yield 4;
}
}
var g = gen();
g.next();//{ value: 1, done: false }
g.return('foo');//{ value: 3, done: false }
g.next(); //{ value: 4, done: false }
g.next(); //{ value: 'foo', done: true }
g.next(); //{ value: undefined, done: true }
複製代碼
在 Generator 函數內部,調用另外一個 Generator 函數。
function* foo() {
yield 1;
yield* bar();
yield 2;
}
function* bar() {
yield 3;
yield 4;
}
var g = foo();
[...g];//[ 1, 3, 4, 2 ]
複製代碼