zoukankan      html  css  js  c++  java
  • ES6 (9):iterator 遍历器

    遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。

    作用:

    一是为各种数据结构,提供一个统一的、简便的访问接口;

    二是使得数据结构的成员能够按某种次序排列;

    三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。

    每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含valuedone两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。

    阮大神的模拟代码:(makeIterator 内部形成了一个闭包)

    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};
        }
      };
    }
    

     其中done:false 和 value:undefined 可以省略。

    当使用for...of循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口

    ES6 规定,默认的 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
            };
          }
        };
      }
    };
    

    原生具备 Iterator 接口的数据结构如下。

    • Array
    • Map
    • Set
    • String
    • TypedArray
    • 函数的 arguments 对象
    • NodeList 对象

    对于原生部署 Iterator 接口的数据结构,不用自己写遍历器生成函数,for...of循环会自动遍历它们。除此之外,其他数据结构(主要是对象)的 Iterator 接口,都需要自己在Symbol.iterator属性上面部署,这样才会被for...of循环遍历

    let obj = {
      data: [ 'hello', 'world' ],
      [Symbol.iterator]() {
        const self = this;
        let index = 0;
        return {
          next() {
            if (index < self.data.length) {
              return {
                value: self.data[index++],
                done: false
              };
            } else {
              return { value: undefined, done: true };
            }
          }
        };
      }
    };
    

     类似数组的对象调用数组的Symbol.iterator方法的例子。

    let iterable = {
      0: 'a',
      1: 'b',
      2: 'c',
      length: 3,
      [Symbol.iterator]: Array.prototype[Symbol.iterator]
    };
    for (let item of iterable) {
      console.log(item); // 'a', 'b', 'c'
    }
    

    如果Symbol.iterator方法对应的不是遍历器生成函数(即会返回一个遍历器对象),解释引擎将会报错。

    使用while循环遍历:

    var $iterator = ITERABLE[Symbol.iterator]();
    var $result = $iterator.next();
    while (!$result.done) {
      var x = $result.value;
      // ...
      $result = $iterator.next();
    }
    

    调用iterator 接口的场合:

    1.解构赋值

    2.扩展运算符

    3.yield*

    4.其他场合

    • for...of
    • Array.from()
    • Map(), Set(), WeakMap(), WeakSet()(比如new Map([['a',1],['b',2]])
    • Promise.all()
    • Promise.race()

    字符串具有iterator 接口。

    覆盖原生的Symbol.iterator方法:

    var str = new String("hi");
    
    [...str] // ["h", "i"]
    
    str[Symbol.iterator] = function() {
      return {
        next: function() {
          if (this._first) {
            this._first = false;
            return { value: "bye", done: false };
          } else {
            return { done: true };
          }
        },
        _first: true
      };
    };
    
    [...str] // ["bye"]
    str // "hi"
    

     遍历器对象的returen、throw:遍历器对象除了具有next方法,还可以具有return方法和throw方法。如果你自己写遍历器对象生成函数,那么next方法是必须部署的,return方法和throw方法是否部署是可选的。

    当使用循环遍历器时使用了break 或者抛出错误时,都会触发执行return 方法。

    function readLinesSync(file) {
      return {
        [Symbol.iterator]() {
          return {
            next() {
              return { done: false };
            },
            return() {
              file.close();
              return { done: true };
            }
          };
        },
      };
    }
    

    for...of循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性。这一点跟for...in循环也不一样。

    let arr = [3, 5, 7];
    arr.foo = 'hello';
    
    for (let i in arr) {
      console.log(i); // "0", "1", "2", "foo"
    }
    
    for (let i of arr) {
      console.log(i); //  "3", "5", "7"
    }
    

     不会返回数组arr的foo属性。

    类似数组的对象

    for...of循环用于字符串、DOM NodeList 对象、arguments对象

    对于字符串来说,for...of循环还有一个特点,就是会正确识别 32 位 UTF-16 字符

    并不是所有类似数组的对象都具有 Iterator 接口,一个简便的解决方法,就是使用Array.from方法将其转为数组(在数组的地方说了,还有Array.of是针对一个序列)。

    let arrayLike = { length: 2, 0: 'a', 1: 'b' };
    
    // 报错
    for (let x of arrayLike) {
      console.log(x);
    }
    
    // 正确
    for (let x of Array.from(arrayLike)) {
      console.log(x);
    }
    

    对于数组的forEach循环,无法中途跳出forEach循环,break命令或return命令都不能奏效

     for...in循环有几个缺点:

    • 数组的键名是数字,但是for...in循环是以字符串作为键名“0”、“1”、“2”等等。
    • for...in循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键
    •  修改为类似数组的对象,使用for...of 循环

    • 某些情况下,for...in循环会以任意顺序遍历键名

     for...of循环相比上面几种做法,有一些显著的优点:

    • 有着同for...in一样的简洁语法,但是没有for...in那些缺点。
    • 不同于forEach方法,它可以与breakcontinuereturn配合使用
    • 提供了遍历所有数据结构的统一操作接口。

     

    成灰之前,抓紧时间做点事!!
  • 相关阅读:
    直方图内最大矩阵
    P1578 奶牛浴场
    P1569 [USACO11FEB]属牛的抗议Generic Cow Prote…
    P1566 加等式
    P1564 膜拜
    P1541 乌龟棋
    P1537 弹珠
    Response.AddHeader使用实例
    LSPCI具体解释分析
    介绍一款开源的类Excel电子表格软件
  • 原文地址:https://www.cnblogs.com/jony-it/p/10969036.html
Copyright © 2011-2022 走看看