一.语法
创建语法:function* + yield
使用语法:next(returnValue).value/done
P.S.迭代器与生成器:function*
定义的东西叫迭代器的生成器(简称生成器),因为调用它返回一个迭代器
二.迭代器的作用
1.函数节流
把时耗多的复杂任务用yield分成小块慢慢做,也叫函数柯里化currying,更多信息请查看JS学习笔记11_高级技巧
2.生成无限序列
比如斐波那契数列
3.方便遍历
不用手动维护内部状态
三.JavaScript生成器实例
1.基本用法
function* fun(a) {
yield a + 1;
yield a * 2;
yield a * 2 + 1;
}
var iter = fun(3);
iter.next();
// => Object {value: 4, done: false}
iter.next();
// => Object {value: 6, done: false}
iter.next();
// => Object {value: 7, done: false}
function* fun(a) {
yield a=a + 1;
yield a=a * 2;
yield a=a * 2 + 1;
}
var iter = fun(3);
// => iter.next();
Object {value: 4, done: false}
iter.next();
// => Object {value: 8, done: false}
iter.next();
// => Object {value: 17, done: false}
iter.next();
// => Object {value: undefined, done: true}
函数执行遇到yield
,先return yield后面的值,再保存函数执行的context(效果类似于保存断点),下一次调用next()
时,恢复context,从yield的下一句开始执行,遇到yield或者return退出
2.高级用法
function* fib() {
var a = 1;
var b = 1;
while (true) {
var current = b;
b = a;
a = a + current;
var reset = yield current;
if (reset) {
a = 1;
b = 1;
}
}
}
var fibSeq = fib();
fibSeq.next();
// => Object {value: 1, done: false}
fibSeq.next();
// => Object {value: 1, done: false}
fibSeq.next();
// => Object {value: 2, done: false}
fibSeq.next();
// => Object {value: 3, done: false}
fibSeq.next();
// => Object {value: 5, done: false}
fibSeq.next(false);
// => Object {value: 8, done: false}
fibSeq.next(true);
// => Object {value: 1, done: false}
fibSeq.next(false);
// => Object {value: 1, done: false}
fibSeq.next(false);
// => Object {value: 2, done: false}
next()
可以接受参数,此参数会被当做yield的返回值传回函数,这样就可以控制函数内部的状态,例如上面的是否reset
3.比较绕的例子
function* fun(a) {
a = yield a + 1;
a = yield a * 2;
yield a * 2 + 1;
}
var iter = fun(3);
iter.next(0);
// => Object {value: 4, done: false}
iter.next(0);
// => Object {value: 0, done: false}
iter.next(1);
// => Object {value: 3, done: false}
类似的:
function* fun(a) {
a = yield a = a + 1;
a = yield a = a * 2;
yield a = a * 2 + 1;
}
var iter = fun(3);
iter.next(0);
// => Object {value: 4, done: false}
iter.next(0);
// => Object {value: 0, done: false}
iter.next(1);
// => Object {value: 3, done: false}
内部执行机制一样,只是写法比较绕
4.其它
此外,迭代器还有throw()
和return()
方法,FF都实现了,Chrome只实现了前者,更多信息请查看MDN Iterators and generators
还有利用1个迭代器生成另一个迭代器的语法,例如:
function* fun() {
yield 1;
yield 2;
}
var iter = fun();
var newIter = (for (i of iter) i * 2);
newIter.next();
// => Object { value: 2, done: false }
newIter.next();
// => Object { value: 4, done: false }
注意,MDN的例子有误,必须是for…of,且必须是圆括号(例子中的for…in和方括号都是错的)
FF支持这种语法,Chrome不支持
三.相关语法
1.for…of、for…in、forEach
for…of用来遍历属性值,for…in用来遍历属性名,forEach是Array.prototype上的方法,能同时遍历属性名和属性值
此外,for…of也能遍历DOM NodeList和自定义的迭代器(function* + yield),是ES6的新东西
实例如下:
var arr = [3, 5, 7];
arr.foo = "hello";
for (var i in arr) {
console.log(i); // logs "0", "1", "2", "foo"
}
for (var i of arr) {
console.log(i); // logs "3", "5", "7"
}
let arr = [3, 5, 7];
arr.foo = "hello";
arr.forEach(function (element, index) {
console.log(element); // logs "3", "5", "7"
console.log(index); // logs "0", "1", "2"
});
2.yield*
与yield
功能类似,也是用来遍历的,但yield*
后面跟迭代器对象,作用是进入后面的迭代器,遍历完再回来,例如:
function* g1() {
yield 2;
yield 3;
yield 4;
}
function* g2() {
yield 1;
yield* g1();
yield 5;
}
var iterator = g2();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: 4, done: false }
console.log(iterator.next()); // { value: 5, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
有了yield*
后,迭代器可以嵌套了,可以“线性”遍历层级结构(当然,前提是拿到所有下层结构的迭代器对象)