zoukankan      html  css  js  c++  java
  • iterator

    iterator-generator

    遍历Array可以采用下标循环,遍历Map和Set就无法使用下标。为了统一集合类型,ES6标准引入了新的iterable类型,Array、Map和Set都属于iteratore类型。
    所有的iterator对象都有next()方法,会返回一个结果对象。该结果对象拥有两个属性,value和done.
    下面的例子模拟生成iterator

    var it = makeIterator(['a', 'b']);
    
    it.next() // { value: "a", done: false }
    it.next() // { value: "b", done: false }
    it.next() // { value: undefined, done: true }
    
    function makeIterator(array) {
      var nextIndex = 0;
      return {
        next: function() {
          return nextIndex < array.length ?
            {value: array[nextIndex++], done: false} :
            {value: undefined, done: true};
        }
      };
    }
    

    上例可以看出,要生成一个iterator很复杂。幸好ES6专门提供了generator用来生成iterator.

    generator

    generator 是一个返回迭代器的函数,与普通函数不同的是声明generators函数要在function后面添加"*",同时函数内部使用es6新增加的关键字yield来控制next()方法的返回值。

    // generator 函数
    function *createIterator(){
        yield 1;
        yield 2;
        yield 3;
    }
    
    // 创建迭代器
    let iterator = createIterator();
    console.log(iterator.next().value); // 1
    console.log(iterator.next().value); // 2
    console.log(iterator.next().value); // 3
    console.log(iterator.next().value); // undefined
    

    从上述代码可以看出每次的yield使用都可以阻止代码的执行,当迭代器调用next()方法后才会继续执行。
    也可以使用函数表达式的形式创建generator函数。

    let createIterator = function *(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}"
    

    注意: 关键字yield只能在generator函数内部使用,即使在生成器内部的函数中也不行。如下代码:

    function *createIterator(items){
        items.forEach(function(item){
            yield item + 1;
        })
    }
    
    let iterator = createIterator([1,2,3]);
    console.log(iterator.next()); // "Uncaught SyntaxError: Unexpected identifier"
    

    在对象中创建生成器。

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

    ES6写法:

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

    iterator与for...of

    Iterator 接口的目的,就是为所有数据结构,提供了一种统一的访问机制,即for...of循环。当使用for...of循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口。
    一种数据只要部署了Iterator接口,就认为这种数据是可遍历的(iterable)。默认的Iterator接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。
    Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。属性名Symbol.iterator,它是一个表达式,返回Symbol对象的iterator属性,这是一个预定义好的、类型为 Symbol 的特殊值,所以要放在方括号内。

    const obj={
    	[Symbol.iterator]: function() {
    		return {
    			next: function() {
    				return {
    					value: 1,
    					done: true
    				}
    			}
    		}
    	}
    }
    obj[Symbol.iterator]().next();  //  {value: 1, done: true}
    

    上面代码,obj具有Symbol.iterator属性,是可遍历的。执行这个属性,会返回一个遍历器对象。该对象的根本特征就是具有next方法。每次调用next方法,都会返回一个代表当前成员的信息对象,具有value和done两个属性。
    Array,Map,Set,String,TypedArray,函数的arguments对象,NodeList对象都具有Iterator接口。可以被for...of遍历。
    注意: 对象(Object)没有默认部署 Iterator 接口。

    let values = [1, 2, 3];
    for (let num of values) {
        console.log(num);   // 1 2 3
    }
    

    for...of首先调用values数组的Symbol.iterator的方法,获取一个迭代器。然后iterator.next()被调用,迭代器的value属性被读出并放入了num变量。num 变量的值开始为 1 ,接下来是2,最后变成 3 。当结果对象的done变成true,循环就退出了。

    上例的代码也可采用下面的方法:

    let values = [1, 2, 3];
    let iter = values[Symbol.iterator]();
    iter.next();    //  {value: 1, done: false}
    iter.next();    //  {value: 2, done: false}
    iter.next();    //  {value: 3, done: false}
    iter.next();    //  {value: undefined, done: true}
    

    ES5中有for...in遍历对象,那么for...in和for...of有什么区别呢?
    for ... in循环由于历史遗留问题,它遍历的实际上是对象的属性名称。一个Array数组实际上也是一个对象,它的每个元素的索引被视为一个属性。当我们手动给Array对象添加了额外的属性后,for ... in循环将带来意想不到的意外效果:

    var a = ['A', 'B', 'C'];
    a.name = 'Hello';
    for (var x in a) {
        console.log(x); // '0', '1', '2', 'name'
    }
    

    采用for...of的写法

    var a = ['A', 'B', 'C'];
    a.name = 'Hello';
    for (var x of a) {
        console.log(x); // 'A', 'B', 'C'
    }
    

    结论:for...in遍历的实际是key,而for...of遍历的是value。

    集合的迭代器

    • entries() :返回一个包含键值对的迭代器;
    • values() :返回一个包含集合中的值的迭代器;
    • keys() :返回一个包含集合中的键的迭代器。

    entries()

    let colors = [ "red", "green", "blue" ];
    let tracking = new Set([1234, 5678, 9012]);
    let data = new Map();
    
    data.set("title", "Understanding ES6");
    data.set("format", "ebook");
    
    for (let entry of colors.entries()) {
        console.log(entry);
    }
    
    for (let entry of tracking.entries()) {
        console.log(entry);
    }
    
    for (let entry of data.entries()) {
        console.log(entry);
    }
    

    以上代码输出结果:

    [0, "red"]
    [1, "green"]
    [2, "blue"]
    [1234, 1234]
    [5678, 5678]
    [9012, 9012]
    ["title", "Understanding ES6"]
    ["format", "ebook"]
    

    values()

    注意:数组不支持values()

    let colors = [ "red", "green", "blue" ];
    let tracking = new Set([1234, 5678, 9012]);
    let data = new Map();
    
    data.set("title", "Understanding ES6");
    data.set("format", "ebook");
    
    for (let value of colors.values()) {
        console.log(value);
    }
    
    for (let value of tracking.values()) {
        console.log(value);
    }
    
    for (let value of data.values()) {
        console.log(value);
    }
    

    以上代码输出结果:

    TypeError: colors.values(...)[Symbol.iterator] is not a function
    1234
    5678
    9012
    "Understanding ES6"
    "ebook"
    

    keys()

    let colors = [ "red", "green", "blue" ];
    let tracking = new Set([1234, 5678, 9012]);
    let data = new Map();
    
    data.set("title", "Understanding ES6");
    data.set("format", "ebook");
    
    for (let key of colors.keys()) {
        console.log(key);
    }
    
    for (let key of tracking.keys()) {
        console.log(key);
    }
    
    for (let key of data.keys()) {
        console.log(key);
    }
    

    以上代码输出结果:

    0
    1
    2
    1234
    5678
    9012
    "title"
    "format"
    
  • 相关阅读:
    视频智能分析系统EasyCVR视频流媒体安防监控云服务实现城市视频智能化应用
    Windows/Android/iOS平台H265编码视频播放器EasyPlayerRTSP中socket选项之keepalive设置介绍
    【解决方案】人脸识别/车牌识别RTSP/GB28181/SDK/Ehome协议视频平台EasyCVR搭建美丽乡村视频监控系统方案
    【操作说明】新版网络穿透+云端组网+远程运维+视频流拉转推平台EasyNTS上云网关管理平台如何安装?
    Windows/Android/iOS平台H265编码视频播放器EasyPlayerPro支持高码率视频播放和D3D画面旋转代码参考
    【操作说明】人脸识别/车牌识别系统视频智能分析平台EasyCVR如何配置开启HTTPS协议?
    程序员们 不要想一辈子靠技术混饭吃
    jsonObject的使用
    程序员们 不要想一辈子靠技术混饭吃
    从 iBatis 到 MyBatis
  • 原文地址:https://www.cnblogs.com/renzhiwei2017/p/7396683.html
Copyright © 2011-2022 走看看