学习笔记 JavaScript ES6 迭代器Iterator

学习内容:

  • 是一种接口机制
  • 让不支持遍历的数据结构“可遍历”

Iterator是一个统一的接口,这个接口有一定的特征要求。要实现这个接口需要return一个对象,并且在这个对象里有一个next()函数,这个next()函数里面要return一个对象,这个对象里面要有一个value和done属性,实现如下:

function makeIterator(arr) {
    let nextIndex = 0
    return {
        next() {
            return nextIndex < arr.length ? {
                value : arr[nextIndex++],
                done : false
            } : {
                value : undefined,
                done : true
            }
        }
    }
}
let it = makeIterator(['a', 'b', 'c'])
console.log(it.next())
console.log(it.next())
console.log(it.next())
console.log(it.next())

--------------------------------------
{value: 'a', done: false}
{value: 'b', done: false}
{value: 'c', done: false}
{value: undefined, done: true}

上面的代码形式和Grenerator一模一样。

当遍历不可遍历的对象时,会报下面的错误:

let classes = {
    allClass : {
        class1 : ['c11','c12','c13'],
        class2 : ['c21','c22','c23'],
        class3 : ['c31','c32','c33']
    }
}

for(let c of classes) {
    console.log(c);
}

-------------------------------------------------------------------------------------
Uncaught TypeError: Invalid attempt to iterate non-iterable instance.
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.

先来看看可遍历的结构特点,看下面的例子,看看数组里包含了一个特别的属性:Symbol(Symbol.iterator)

let arr = ['a', 'b', 'c']
console.log(arr);

----------------------------
(3) ['a', 'b', 'c']
0: "a"
1: "b"
2: "c"
length: 3
[[Prototype]]: Array(0)
at: ƒ at()
concat: ƒ concat()
constructor: ƒ Array()
copyWithin: ƒ copyWithin()
entries: ƒ entries()
every: ƒ every()
fill: ƒ fill()
filter: ƒ filter()
find: ƒ find()
findIndex: ƒ findIndex()
findLast: ƒ findLast()
findLastIndex: ƒ findLastIndex()
flat: ƒ flat()
flatMap: ƒ flatMap()
forEach: ƒ forEach()
includes: ƒ includes()
indexOf: ƒ indexOf()
join: ƒ join()
keys: ƒ keys()
lastIndexOf: ƒ lastIndexOf()
length: 0
map: ƒ map()
pop: ƒ pop()
push: ƒ push()
reduce: ƒ reduce()
reduceRight: ƒ reduceRight()
reverse: ƒ reverse()
shift: ƒ shift()
slice: ƒ slice()
some: ƒ some()
sort: ƒ sort()
splice: ƒ splice()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
unshift: ƒ unshift()
values: ƒ values()
Symbol(Symbol.iterator): ƒ values() // <<=== 注意看这里!!!!!!!!!
Symbol(Symbol.unscopables): {copyWithin: true, entries: true, fill: true, find: true, findIndex: true, …}
[[Prototype]]: Object

再看看哪些原生的数据结构具备Iterator接口,这些都可以直接用for...of去遍历:

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

那么如何让一个不可遍历的结构变成可遍历的结构呢?

还是要再明确两个概念:

可迭代协议:当前的对象上面是否有一个Symbol.iterator,有就可以用for...of迭代

迭代器协议:

必须符合这样的条件:return { next( return {value, done} ) }

看改造代码的实现:

let classes = {
  allClass: {
    class1: ["c11", "c12", "c13"],
    class2: ["c21", "c22", "c23"],
    class3: ["c31", "c32", "c33"],
  },
};

classes[Symbol.iterator] = function () {
  let allClass = this.allClass;
  let keys = Reflect.ownKeys(allClass);
  let values = [];
  return {
    next() {
      if (!values.length) {
        if (keys.length) {
          values = allClass[keys[0]];
          keys.shift(); // 弹出,删除数组的第一个元素
        }
      }
      return {
        done: !values.length,
        value: values.shift(),
      }
    }
  }
}

for(let c of classes) {
    console.log(c);
}

---------
c11
c12
c13
c21
c22
c23
c31
c32
c33

上一篇学习笔记学习笔记 JavaScript ES6 异步编程Grenerator用法_白鸽同学的博客-CSDN博客中,也是返回一个对象,对象里有next(),并且next()的返回值也有value,done,所以可以直接在iterator里直接使用Grenerator,这样就不需要自己定义next方法。

使用Grenerator实现如下:

let classes = {
  allClass: {
    class1: ["c11", "c12", "c13"],
    class2: ["c21", "c22", "c23"],
    class3: ["c31", "c32", "c33"],
  },
};

classes[Symbol.iterator] = function* () {
  let allClass = this.allClass;
  let keys = Reflect.ownKeys(allClass);
  let values = [];
  while (1) {
    if (!values.length) {
      if (keys.length) {
        values = allClass[keys[0]];
        keys.shift();
        yield values.shift();
      } else {
        return false;
      }
    } else {
      yield values.shift();
    }
  }
};
for (let c of classes) {
  console.log(c);
}
---------
c11
c12
c13
c21
c22
c23
c31
c32
c33

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值