JavaScript生成器

一.语法

创建语法: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*后,迭代器可以嵌套了,可以“线性”遍历层级结构(当然,前提是拿到所有下层结构的迭代器对象)

参考资料

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

code