• 迭代器与生成器


    一、迭代器的特征

    迭代器有一个next()方法,每次调用时会返回一个对象,该对象的结构为{value:xxx,done:true},其中value表示下次应该返回的值,done表示是否还有值可提供。

    当没有值可提供时,done为true,如果迭代器在迭代结束时使用了return xxx,则value为xxx,否则为undefined。

    function createIterator(items) { 
        var i = 0;
         return { 
             next: function() { 
                 var done = (i >= items.length); 
                 var value = !done ? items[i++] : undefined; 
                 return { done: done, value: value }; 
            } 
        }; 
    } 
    var iterator = createIterator([1, 2, 3]); 
    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: undefined, done: true }" 
    // 之后的所有调用 
    console.log(iterator.next()); // "{ value: undefined, done: true }"

    二、迭代器的作用

    • 用于依序访问可迭代的对象,可迭代的对象为:Array、Map、Set、String、TypedArray、函数的 arguments对象、NodeList 对象
    • 可迭代对象( iterable )内部有一个 Symbol.iterator 属性。这 个 Symbol.iterator 知名符号定义了为指定对象返回迭代器的函数。
    • 集合类型有三种迭代器:

                 entries() :返回一个包含键值对的迭代器,此迭代器是Map类型默认的迭代器;Array把下标做为key;而Set则把值作为key

                 values() :返回一个包含集合中的值的迭代器,此迭代器是Set与Array的默认迭代器

                 keys() :返回一个包含集合中的键的迭代器。

    let colors = [ "red", "green", "blue" ];
    for (let entry of colors.entries()) { 
        console.log(entry); 
    }
    • 当使用for of访问对象时,如果没有显示指定使用哪个迭代器,则调用每种类型默认的。
    • 在for of中可以使用break语句跳出for of,可使用throw new Error('xxx')终止程序继续运行

    三、使用生成器创造迭代器

     在function后面加一个*号,然后在函数内部使用yield标识符指定调用next时应该返回的值

    function *createIterator(items) { 
        for (let i = 0; i < items.length; i++) { 
            yield items[i]; 
        } 
    } 
    let iterator = createIterator([1, 2, 3]); 
    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: undefined, done: true }" 
    // 之后的所有调用 
    console.log(iterator.next()); // "{ value: undefined, done: true }"

    生成器一是特殊的函数,因此可以作为对象的方法

    var o = { 
        *createIterator(items) { 
            for (let i = 0; i < items.length; i++) { 
                yield items[i]; 
            } 
        } 
    }; 
    let iterator = o.createIterator([1, 2, 3]);

    yield关键字不能放在生成器内部的函数中,下面代码是错误的

    function *createIterator(items) { 
        items.forEach(function(item) { 
            // 语法错误 
            yield item + 1; 
        }); 
    }

    四、把不可迭代的对象变成可迭代的

    默认情况下,我们自己创建的对象是不可迭代的,可以自己定义一个生成器让对象变成可迭代对象

    let collection = { 
        items: [], 
        *[Symbol.iterator]() { 
            for (let item of this.items) { 
                yield item; 
            } 
        } 
    }; 
    collection.items.push(1); 
    collection.items.push(2);
    collection.items.push(3); 
    for (let x of collection) { 
        console.log(x); 
    }

     五、迭代器的next和生成器的yield进行通讯

    1. 在生成器中,当下一个yield的值依赖上一个yield的结果进行计算时,可以使用迭代器的next(xxx)方法传值来覆盖上一个yield的结果,例子

    function *createIterator() {
     let first = yield 1;
     let second = yield first + 2; // 4 + 2
     yield 3; 
    }
    let iterator = createIterator();
    console.log(iterator.next());  // 第1个next传值不会生效,因为没有上一个yield
    console.log(iterator.next(4)); // first变成了4,结果为"{ value: 6, done: false }"
    console.log(iterator.next(5)); // 第3条yield不依赖上一条yield的结果进行计算,传值无效
    console.log(iterator.next()); // "{ value: undefined, done: true }"

     2.通过iterator.throw()向生成器传递错误来阻止后续的迭代

    function *createIterator() {
     let first = yield 1;
     let second = yield first + 2; // 4 + 2
     yield 3; 
    }
    let iterator = createIterator();
    console.log(iterator.next());  // 第1个next传值不会生效,因为没有上一个yield
    console.log(iterator.throw('后面的别运行了'));
    console.log(iterator.next(5)); // 不会执行
    console.log(iterator.next()); //  不会执行

     3.在生成器中通过try catch屏蔽错误

    下面的代码在第二个next前抛出了错误,也就是生成器在执行第一条yield时会出现错误,采用try catch处理后,生成器可以继续向下运行

    function *createIterator() {
     let first;
     try{
        first = yield 1;
     }
     catch(ex){
         first=100
     }
     let second = yield first + 2; // 4 + 2
     yield 3; 
    }
    let iterator = createIterator();
    console.log(iterator.next());
    console.log(iterator.throw('后面的别运行了')); //{value: 102, done: false}
    console.log(iterator.next(5)); //没有依赖上一个yield的结果进行计算,返回 {value: 3, done: false}
    console.log(iterator.next()); // {value: undefined, done: true}

     六、生成器合并

    1. 可以将多个生成器合并成一个,方法是新写一个生成器函数,然后把生成器内部的各个yield分别指向不同的生成器。

    合并后程序是按顺序执行的,即先把第一个生成器内的所有yield执行完再执行下一个。

    function *createOne() {
       for(let i=4;i<7;i++){
           yield i
       }
    }
    function *createTwo() {
       for(let i=7;i<10;i++){
           yield i
       }
    }
    function *createAll(){
        yield *createOne();
        yield *createTwo();
    }
    var iterator=createAll();
    for(let i of iterator){
        console.log(i)
    }

    2.后一个生成器依赖前一个生成器的结果

    可以在前一个迭代器中使用return返回一个迭代结束后的值,然后把该值传给第二个迭代器的yield使用

    function *createOne() {
       for(let i=4;i<7;i++){
           yield i
       }
       return 101;
    }
    function *createTwo(oneResult) {
       for(let i=7;i<10;i++){
           yield i+oneResult
       }
    }
    function *createAll(){
        let result=yield *createOne();
        yield *createTwo(result);
    }
    var iterator=createAll();
    for(let i of iterator){
        console.log(i)
    }

     七、任务执行器

    每次调用next()时都会执行yield上面的语句,还有一条yield后面的语句。

    function* gen(){
        console.log('one')
        yield 1;
        console.log('b')
        yield 2;
        yield 3;
    }
    var t=gen();
    console.log(t.next());
    console.log(t.next());

     那么我们能不能做一个任务运行器自动执行next呢?答案是使用递归

    let run=function*(generator){
        let task=generator();
        let result=task.next();启动任务运行器
        function autoNext(){
            if(!task.done){
                result=task.next(result.value)
                step();//递归执行下一个next()
            }
        }
        autoNext();
    }
    run(function*() {
        let value = yield 1;
        console.log(value); // 1
        value = yield value + 3;
        console.log(value); // 4
    });
  • 相关阅读:
    vue中局部过滤器的使用
    elementui中switch开关的回调的使用
    css居中的一些方法
    elementui默认样式修改的几种方法
    git查看远程分支,并且切换到远程的分支
    elementui form resetFields方法 无法重置表单
    vue组件使用vuex中的方法报错,报unknown mutation type的错误
    offSet和client和scroll
    842. Split Array into Fibonacci Sequence能否把数列返回成斐波那契数列
    662. Maximum Width of Binary Tree二叉树的最大宽度
  • 原文地址:https://www.cnblogs.com/94pm/p/9653152.html
Copyright © 2020-2023  润新知