• es6的迭代器和生成器


    迭代器
    es6新增的特性,之前没有,其语法借鉴了Python、 Java、 C++。

    Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法,可用于迭代List 和 Set 等集合。

    实现了迭代器接口的类,都可以使用next方法来取元素。

    比如:字符串、数组、映射、集合(Set、Map)、arguments 对象、NodeList 等 DOM 集合类型。



    表现

    const set = new Set();
    set.add('共');
    set.add('产');
    set.add('党');
    
    const setGen = set[Symbol.iterator](); // 取出迭代器:[Symbol.iterator]创建迭代器的工厂函数
    const {done,value:fisrstEl} = setGen.next(); // 调用next取出第一个元素 
    let hasNext = !done; // 判断有没有元素标识
    hasNext &&  console.log(fisrstEl, hasNext);
    while(hasNext){
        const {done, value} = setGen.next();
        hasNext = !done;
        hasNext && console.log(value, hasNext);
    }

    由上可以推断出:检查是否可以迭代 只需要判断是否存在默认迭代器属性的工厂函数[Symbol.iterator]即可知道

    let num = 1;
    let obj = {};
    let str = 'abc';
    // 这两种类型没有实现迭代器工厂函数
    console.log(num[Symbol.iterator]); // undefined
    console.log(obj[Symbol.iterator]); // undefined
    // 这些类型都实现了迭代器工厂函数
    console.log(str[Symbol.iterator]); // f values() { [native code] }

    自己实现一个迭代器

    举例,比如针对Object不支持迭代,所以我们帮其实现一个

    const obj = {
        name:'小明',
        age:20,
        [Symbol.iterator](){ // 迭代器工厂函数(用来生成迭代器的)
            let index = 0;
            return {
                next:()=>{
                    const v = Object.keys(this)[index++];
                    return {done:!Boolean(obj), value:this[v]}
                }
            }
        }
    };
    const objGen = obj[Symbol.iterator]();
    console.log(objGen.next());  // {done: false, value: "小明"}
    console.log(objGen.next());  // {done: false, value: 20}
    console.log(objGen.next());  // {done: true, value: undefined}

    利用生成器来简化迭代器的定义
    如上代码,写起来很麻烦,比如我们需要一个index索引,通过自增来确定上一个和下一个。  我们还要写两个嵌套返回等等
    所以es为我们提供一个内置办法,可以简化这些操作。

    const obj = {
        name:'小明',
        age:20,
        [Symbol.iterator]:function* (){ // 迭代器的生成器(用来取代迭代器工厂函数)
            for (let index = 0; index < Object.keys(this).length; index++) {
                const key =  Object.keys(this)[index];
                yield this[key]
            }
        }
    };
    const objGen = obj[Symbol.iterator]();
    console.log(objGen.next());  // {done: false, value: "小明"}
    console.log(objGen.next());  // {done: false, value: 20}
    console.log(objGen.next());  // {done: true, value: undefined}

    利用生成器来模拟同步代码

    yield 关键字可以让生成器停止和开始执行,也是生成器最有用的地方。生成器函数在遇到 yield
    关键字之前会正常执行。遇到这个关键字后,执行会停止,函数作用域的状态会被保留。停止执行的生
    成器函数只能通过在生成器对象上调用 next()方法来恢复执行

    这是红宝书 第4版的话,我们可以利用这个特性来模拟同步ajax

    比如我有两个接口,代码如下:
    api.js

    /*
    * 获取老师个人信息
    **/
    let getTcBasic = (tid) => {
        const res = {
            1: {
                id: 1,
                name: '小明',
                age: 21,
                sex: '男',
                twfid: 2001
            },
            2: {
                id: 2,
                name: '小蓝',
                age: 20,
                sex: '男',
                twfid: 2002
            }
        }
        return new Promise((resolve) => {
            setTimeout(function () {
                resolve(res[tid]);
            }, 1000);
        });
    };
    
    
    /*
    * 获取老师配偶信息
    **/
    const getTcWife = (twfid) => {
        const res = {
            2001: {
                id: 2001,
                name: '小红',
                age: 18
            },
            2002: {
                id: 2002,
                name: '小白',
                age: 19
            }
        }
        return new Promise((resolve) => {
            setTimeout(function () {
                resolve(res[twfid]);
            }, 2000);
        });
    }

    实现代码如下

    /*
    * 循环运行gen(中yield工具 类似于三方包co的作用)
    **/
    const runGen = (gen, res) => {
        const result = gen.next(res);
        if (result.done) return;
        result.value.then((res) => {
            runGen(gen, res);
        });
    }
    
    /*
    * 一个含有ajax请求的迭代器实例
    * tip: 注意观察写法是不是很同步
    **/
    function getTcInfoGen(tid){
        // 返回generator对象 (generator实例是可以被next分段执行,可以理解为可以分段执行的函数)
        const generator = function* (){
            console.log('第1次执行');
            const tcBasicInfo = yield getTcBasic(tid);
            
            console.log('第2次执行');
            const tcWifeInfo = yield  getTcWife(tcBasicInfo.twfid);
            
            console.log('第3次执行');
            console.log({...tcBasicInfo,tcWifeInfo});
        }
        return generator();
    }
    
    /*
    * 获取老师全部信息
    **/
    const getTcInfo = (tid) => {
        console.log('请求中...');
        const gen = getTcInfoGen(tid);
        runGen(gen)
    };
    getTcInfo(1);

    当然这只是js中同步需求的过度方案,最终还是要使用更加语义化es7中的async和await来  代替 function*和yield。
    es7中的async和await是参考 function*和yield并做出了部分改良实现的

    参考某位博主说的:
    虽然Generator将异步操作表示得很简洁,但是流程管理却不方便(即何时执行第一阶段、何时执行第二阶段)。
    此时,我们便希望能出现一种能自动执行Generator函数的方法。我们的主角来了async/await。



    重新认识一下for...of

    for循环的内部机制是:将可迭代对象利用next进行取值,然后将值再给调用者。
    这也就意味着只有实现了原生的 iterator 接口,才能使用 for...of。
    所以for...of不支持对象迭代。
    那我们刚才给对象添加了迭代对象,自然是可以的,运行代码如下

    const obj = {
        name:'小明',
        age:20,
        [Symbol.iterator]:function* (){ // 迭代器的生成器(用来取代迭代器工厂函数)
            for (let index = 0; index < Object.keys(this).length; index++) {
                const key =  Object.keys(this)[index];
                yield this[key]
            }
        }
    };
    
    for (const iterator of obj) {
        console.log(iterator); 
    }
    // 小明
    // 20

    相比普通的for循环(也称计数循环),通过迭代器来实现的循环办法更加的方便和自由,因为可以自己实现迭代器,决定结果值。其他好处红宝书第4版所说:

    1、迭代之前需要事先知道如何使用数据结构。
    2、遍历顺序并不是数据结构固有的

    其它

    java语言更加完整和系统,所以用java去辅助理解js更加的通俗易懂和完整。

    参考java的迭代器相关知识:https://www.runoob.com/java/java-iterator.html

    js的集合有哪些?

    js集合的概念是在es6中提出的,es5没有 这个概念,同样也是借鉴java等语言进行归纳。
    原先可用于存储的有Object和Array,但是这些是有缺点的,至于哪些缺点,自己百度吧。
    所以js又发明了新的概念,集合。
    Map(以及其子类WeakMap)和Set(以及其子类WeakSet)

    贴一张java集合的图

    会发现,java的集合更加的全面,这也意味着,JavaScript以后还会对集合进行补充(因为存在这些集合类不是没有道理的,所以同样的js也会面临这些问题。所以慢慢扩充是必然的结果)。

    同时也发现集合也都是实现了迭代器接口的。那就意味着集合都是可以迭代的,比如使用next()或者使用for...of。

  • 相关阅读:
    [WPF VTK]三维图形开发基础(一)
    WP开发(一)
    [WPF VTK]三维图形开发基础(四)

    WIN8 下IE突然无法打开(管理员权限可打开)
    [WPF VTK]三维图形开发基础(三)
    堆排序、快排的坑
    双向链表之插入
    [WPF VTK]三维图形开发基础(二)
    [转载]Android界面设计学习日志(一)
  • 原文地址:https://www.cnblogs.com/dshvv/p/15167530.html
Copyright © 2020-2023  润新知