Skip to content

引入 ​

  • 本文摘自

Iterator和for...of ​

  1. ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)

    js
        let arr = ['a', 'b', 'c'];
        let iter = arr[Symbol.iterator]();
        iter.next() // { value: 'a', done: false }
        iter.next() // { value: 'b', done: false }
        iter.next() // { value: 'c', done: false }
        iter.next() // { value: undefined, done: true }
  2. 原生具备 Iterator 接口的数据结构如下

    • Map Set String Array TypedArray 函数的arguments对象 NodeList对象
    • 本质上,遍历器是一种线性处理,对于任何非线性的数据结构,部署遍历器接口,就等于部署一种线性转换.所以对象没有默认部署 Iterator 接口
  3. 调用 Iterator 接口的场合

    • 解构赋值
    • 扩展运算符
    • yield*
    • 其他场合(for…of Array.from() Map(), Set(), WeakMap(), WeakSet()(比如new Map([['a',1],['b',2]]))) Promise.all() Promise.race() )
  4. 修改默认的iterator接口

    js
          let str = new String('4522688899')
          str[Symbol.iterator] = function () {
            return {
              index: 0,
              next: function () {
                return this.index < str.length ? {
                  value: str[this.index++] + 1,
                  done: false
                } : {
                  done: true
                }
              },
            };
          };
          console.log([...str]);  //["41", "51", "21", "21", "61", "81", "81", "81", "91", "91"]

for...of与for...in ​

  1. for...in循环读取键名,for...of循环读取键值。如果要通过for...of循环,获取数组的索引,可以借助数组实例的entries方法和keys方法
    js
        var arr = ['a', 'b', 'c', 'd']; 
        let obj = {
              'a': 111,
              'b': 221,
              '0': 555,
              1.1: 666,
                2: 777,
              'c': 331,
            }
        for (let key in obj) {
          console.log(key); // "0","2","a","b","1.1","c"
        }
        for (let a in arr) {
          console.log(a); // 0 1 2 3
        }
        for (let a of arr) {
          console.log(a); // a b c d
        }
  2. for...of循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性。这一点跟for...in循环也不一样
    js
        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"
          }
  3. Set 和 Map 结构也原生具有 Iterator 接口,可以直接使用for...of循环。Set 结构遍历时,返回的是一个值,而 Map 结构遍历时,返回的是一个数组,该数组的两个成员分别为当前 Map 成员的键名和键值。

计算生成的数据结构 ​

  1. entries() 返回一个遍历器对象,用来遍历[键名, 键值]组成的数组。对于数组,键名就是索引值;对于 Set,键名与键值相同。Map 结构的 Iterator 接口,默认就是调用entries方法。
  2. keys() 返回一个遍历器对象,用来遍历所有的键名。
  3. values() 返回一个遍历器对象,用来遍历所有的键值。
    js
         let arr = ['a', 'b', 'c'];
          arr.foo = 11
          for (let pair of arr.entries()) {
            console.log(pair);
          }
          // [0, 'a']
          // [1, 'b']
          // [2, 'c']
  4. 对于字符串来说,for...of循环还有一个特点,就是会正确识别 32 位 UTF-16 字符
    js
        for (let x of 'a\uD83D\uDC0A') {
          console.log(x);
        }
        // 'a'
        // '🐊'
  5. 对于普通的对象,for...of结构不能直接使用,会报错,必须部署了 Iterator 接口后才能使用.
  • 一种解决方法是,使用Object.keys方法将对象的键名生成一个数组,然后遍历这个数组
    js
          let someObject = {
            edition: 6,
            committee: "TC39",
            standard: "ECMA-262"
          };
          for (var key of Object.keys(someObject)) {
            console.log(key + ': ' + someObject[key]);      //edition: 6  
          }                                                 //committee: TC39
                                                            //standard: ECMA-262
  • 另一个方法是使用 Generator 函数将对象重新包装一下。
    js
        function* entries(obj) {
          for (let key of Object.keys(obj)) {
            yield [key, obj[key]];
          }
        }
        for (let [key, value] of entries(obj)) {
          console.log(key, '->', value);
        }
        // a -> 1
        // b -> 2
        // c -> 3

与其他遍历语法的比较 ​

  1. 以数组为例,JavaScript 提供多种遍历语法。最原始的写法就是for循环,写法比较麻烦.
  2. 因此数组提供内置的forEach方法,这种写法的问题在于,无法中途跳出forEach循环,break命令或return命令都不能奏效
  3. for...in循环可以遍历数组的键名
    • 数组的键名是数字,但是for...in循环是以字符串作为键名“0”、“1”、“2”等等
    • for...in循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键
    • 某些情况下,for...in循环会以任意顺序遍历键名
    • 总之,for...in循环主要是为遍历对象而设计的,不适用于遍历数组
  4. for...of循环相比上面几种做法,有一些显著的优点
    • 有着同for...in一样的简洁语法,但是没有for...in那些缺点
    • 不同于forEach方法,它可以与break、continue和return配合使用
    • 提供了遍历所有数据结构的统一操作接口
      js
          for (var n of fibonacci) {
            if (n > 1000)
              break;
            console.log(n);
          }