ES6的很多特性都跟Generator扯上关系,而且实际用处比较广, 包含了任何需要异步的模块, 比如ajaxfilesystem, 或者数组对象遍历等都可以用到;

  Generator函数和普通的函数区别有两个, 1:function和函数名之间有一个*号, 2:函数体内部使用了yield表达式;比如这样:

function* gen() {
    yield "1";
    yield "2"
}

   这个玩意儿如果运行的话,会返回一个Iterator实例, 然后再执行Iterator实例的next()方法, 那么这个函数才开始真正运行, 并把yield后面的值包装成固定对象并返回,直到运行到函数结尾, 最后再返回undefined; 

"use strict";
function* fibonacci() {
    yield 1;
    yield 2;
}

var it = fibonacci();
console.log(it);          // "Generator {  }"
console.log(it.next());   // 1
console.log(it.next());   // 2
console.log(it.next()); //undefined

 

  ES6新特性:Javascript中Generator(生成器)

  yield

  Generator函数返回的Iterator运行的过程中,如果碰到了yield, 就会把yield后面的值返回, 此时函数相当于停止了, 下次再执行next()方法的时候, 函数又会从上次退出去的地方重新开始执行;

  如果把yieldreturn一起使用的话, 那么return的值也会作为最后的返回值, 如果return语句后面还有yield, 那么这些yield不生效:

function* gen() {
    yield 0;
    yield 1;
    return 2;
    yield 3;
};
let g = gen();
console.log(g.next(),g.next(),g.next(),g.next());
//输出:{ value: 0, done: false } { value: 1, done: false } { value: 2, done: true } { value: undefined, done: true }

 

  我们也不能在非Generator函数中使用yield,比如:

<script>
var arr = [1, [[2, 3], 4], [5, 6]];
var flat = function* (a) {
    a.forEach(function (item) {
        if (typeof item !== 'number') {
            yield* flat(item);
        } else {
            yield item;
        }
    })
};

for (var f of flat(arr)){
    console.log(f);
}
</script>

 

  上面的demo因为callback是一个普通函数, 所以编译的时候直接抛出错误提示, 我们需要改成在Generator的函数体中:

<script>
var arr = [1, [[2, 3], 4], [5, 6]];
var flat = function* (a) {
    var length = a.length;
    for (var i = 0; i < length; i++) {
        var item = a[i];
        if (typeof item !== 'number') {
            yield* flat(item);
        } else {
            yield item;
        }
    }
};
for (var f of flat(arr)) {
    console.log(f);
}
</script>

 

  或者有个更奇怪的方法,我们把数组的forEach改成Generator函数:

<script>
var arr = [1, [[2, 3], 4], [5, 6]];
Array.prototype.forEach = function* (callback) {
    for(var i=0; i<this.length; i++) {
        yield* callback(this[i],i ,this[i]);
    }
}
var flat = function* (a) {
    yield* a.forEach(function* (item) {
        if (typeof item !== 'number') {
            yield* flat(item);
        } else {
            yield item;
        }
    })
};

for (var f of flat(arr)){
    console.log(f);
}
</script>

 

  而且Iterator的return的值不会被for...of循环到 , 也不会被扩展符遍历到, 以下Demo的return 2yield 3完全不生效了, 这个是要注意的;

function* gen() {
    yield 0;
    yield 1;
    return 2;
    yield 3;
};
let g = gen();
console.log([...g]); //输出:[ 0, 1 ]
for(let foo of g) {
    console.log( foo ); //输出 0, 1
}

  yield*

  yield*这种语句让我们可以在Generator函数里面再套一个Generator, 当然你要在一个Generator里面调用另外的Generator需要使用: yield* 函数() 这种语法, 都是套路啊:

function* foo() {
    yield 0;
    yield 1;
}
function* bar() {
    yield 'x';
    yield* foo();
    yield 'y';
}
for (let v of bar()){
    console.log(v);
};

 

  next()方法

  Generator函数返回的Iterator执行next()方法以后, 返回值的结构为:

{
    value : "value", //value为返回的值
    done : false //done的值为一个布尔值, 如果Interator未遍历完毕, 他会返回false, 否则返回true;
}

 

  所以我们可以模拟一个Generator生成器, 利用闭包保存变量, 每一次执行next()方法, 都模拟生成一个{value:value,done:false}的键值对:

function gen(array){
    var nextIndex = 0;
    return {
        next: function(){
            return nextIndex < array.length ?
            {value: array[nextIndex++], done: false} :
            {value: undefined, done: true};
        }
    };
};

var it = gen(["arr0", "arr1", "arr2", "arr3"]);
console.log( it.next() );
console.log( it.next() );
console.log( it.next() );
console.log( it.next() );
console.log( it.next() ); 

  再浪一点的话,我们也可以模拟一个对象的Iterator, 因为本身对象是没有Iterator的, 我们为对象添加[Symbol.iterator]方法:

<script>
var itObj = {
    0:"00",
    1:"11",
    2:"22",
    3:"33",
    length : 4,
    [Symbol.iterator]() {
        const _this = this;
        let index = 0;
        return {
            next() {
                if(index< _this.length) {
                    return {
                        value : _this[index++],
                        done : false
                    }
                }else{
                    return {
                        value : undefined,
                        done : true
                    }
                }
            }
        }
    }
};
console.log([...itObj]);
</script>
View Code

相关文章: