Generator 函数语法
Generator
函数是ES6提供的一个生成器,声明生成器需要在函数名称前加 *
号。
执行Generator
函数会返回一个迭代器(是一个对象),在生成器函数内部,通过 yield
或 yield*
,将当前的生成器的控制交给外部,在外部调用 next
或 return
或 throw
方法在把控制权还给生成器函数,并且还可以给生成器函数传递参数
定义Generator
和普通函数定义时一样,唯一不同就是在function
后面添加*
,因为ES6中没有明确规定*
的位置,所以*
号只要在function
和function name
中间都可以
function * sum(){
yield 1 + 2;
}
yield 和 yield 表达式*
yield
和 yield*
是在生成器函数内部使用的,生成器通过 yield
向外部返回值。只要生成器调用了 next
方法,就会返回一个 IteratorResult
对象。
- 遇到
yield
就会暂停执行 并且将yield
后面的值作为返回对象的value
属性 - 下一次调用
next
方法会在继续往下执行,直到遇到下一个yield
,没有的话,就直到return
,并将return
后面的值作为value
的值 - 没有
return
也value的为undefined
- ⚠️
yield
只能在Generator
函数中,在其他函数会报错
该对象结构为:
{value:undefined,done:true}
其中,value
表示此次迭代的结果,done
表示是否已经迭代结束。
function* gen(){
yield 1;
yield 2;
}
let g = gen();
g.next();//{value:1,done:true}
g.next();//{value:2,done:true}
g.next(); //{value:undefined,done:true}
yield
后面可以不带任何表达式,此时的 value
返回 undefined
。
function *gen(){
yield
}
let g = gen();
g.next(); // {value:undefined,done:true}
生成器通过使用 yield*
表达式可以委托给另外一个可以迭代的对象,包括生成器。
委托给 js 内置的迭代对象
function *fn(){
yield 1;
yield* [2,3]
}
let f = fn();
f.next(); //{value:1,done:false}
f.next(); //{value:2,done:false}
f.next(); //{value:3,done:false}
f.next(); //{value:undefined,done:true}
委托给一个生成器
function * sum(){
yield* fn();
yield* [4,5]
yield 6;
}
let s = sum();
s.next(); //{value: 1, done: false}
s.next(); //{value: 2, done: false}
s.next(); //{value: 3, done: false}
s.next(); //{value: 4, done: false}
s.next(); //{value: 5, done: false}
s.next(); //{value: 6, done: false}
s.next(); //{value:undefined,done:true}
如果Generator
函数不搭配yield
表达式,就只是一个单纯的暂缓函数
function *f(){
console.log('你好');
}
let g = f();
g.next() //你好
next、return 和 throw 方法
在生成器外部通过这3个方法控制生成器函数内部的执行过程。
next
在next
中传递参数,第一次传递参数是无用的。
在生成器外部可以给 next
方法传递一个参数,这个参数会被当做上一个的 yield
表达式的返回值,如果不传递参数,那么 yield
表达式返回 undefined
。
function * say() {
let a = yield 'hello';
console.log('a',a);
let b = yield 'world';
console.log('b',b);
let c = yield 'zfpx';
console.log(c);
}
let it = say();
it.next(100); // 第一次next传递参数 是无意义的
it.next(200);
it.next(300);
function *fn(){
console.log(yield)
console.log(yield)
console.log(yield)
}
let f = fn();
f.next(); // 执行第一个 yield 表达式
f.next("hello"); // 第一个 yield 表达式的返回值是 'hello', 打印出 'hello'
f.next("world"); // 第二个 yield 表达式的返回值是 'world', 打印出 'world'
f.next(); // {value:undefined,done:true}
执行过程
- 第一次调用
next
碰到yield
就暂停了。此时返回的{value:1,done:false},不会给a赋值 - 在调用
next
的时候传入100
这时,a就是100 - …
return
return
是结束生成器的,并且返回一个 IteratorResult
,其中 done
是 true
,value
是 return
的返回值,默认是 undefined
。
function * fn(){
yield 1;
yield 2;
yield 3;
}
let f = fn();
f.next(); //{value:1,done:false}
f.return('100'); //{value:'100',done:true}
f.next(); // {value:undefined,done:true}
throw
在生成器外部可以通过给 throw
方法传入一个参数,该参数会由 catch
语句捕获,如果不传入任何参数。catch
语句捕获到的将会是 undefined
,catch
语句捕获到之后会恢复生成器的执行,返回带有的 IteratorResult
(只返回 1 次)。
function *g(){
try{
yield 1;
yield 2;
yield 3;
}catch(e){
console.log(e)
}
}
let a = g();
a.next(); //{value: 1, done: false}
a.throw(new Error("error!")); //Error: error!
//{value: undefined, done: true}
a.next(); //{value: undefined, done: true}
和 Symol.iterator的关系
Symbol.iterator 是一个函数,就是当前数据结构默认的迭代器生成函数,执行这个函数,就会返回一个迭代器,至于属性名Symbol.iterator
,它是一个表达式,返回 Symbol
对象的iterator
属性,这是预定好的类型为 Symbol 的特殊值,所以要放在[]中
```
const obj ={
[Symbol.iterator]:function(){
return {
next:function(){
return { value:1 ,done:true}
}
}
}
}
```
没有Symbol.iterator的不可以被for of 遍历,显然 object没有
- Object 因为不确定那个属性先遍历,那个属性后遍历,需要开发者手动制定。
原生俱备iterator接口的数据结构 - Arrary
- Map
- Set
- String
- TypeArray
- 函数的 arguments 对象
- NodeList 对象
Array
- 原生就有
iterator
接口,部署在Array实例的Symbol.iterator
属性上面
let ary = [1,2,4,5];
let it = ary[Symbol.iterator]()
it.next() //{value: 1, done: false}
it.next() // {value: 2, done: false}
it.next() // {value: 4, done: false}
it.next() // {value: 5, done: false}
it.next() // {value: undefined, done: true}
String
- 原生就有
iterator
接口,部署在String实例的Symbol.iterator
属性上面
var someString = "hi";
typeof someString[Symbol.iterator]
// "function"
var iterator = someString[Symbol.iterator]();
iterator.next() // { value: "h", done: false }
iterator.next() // { value: "i", done: false }
iterator.next() // { value: undefined, done: true }