手搓自引用迭代器

一、关于迭代器的问题

在学习迭代器的过程中学习到了这样一个知识点:

因为每个迭代器也实现了Iterable接口 , 所以它们也可以用在任何期待可迭代对象的地方 (例如for-of)

1.迭代器的基本概念

可能有些人看到上面的这句话会有些懵 , 我先将相关的知识简单介绍一下:

1.什么是迭代器?

答: 迭代器是一种跨类型的通用迭代方法。简单来说就是不论数据是什么类型,只要符合迭代器协议,就可以通过迭代器进行迭代。

2.怎样的数据符合可迭代协议?

答:可迭代协议就是Iterable接口,因此只要实现了Iterable接口的数据类型就符合可迭代协议。

3.怎样实现Iterable接口?

答:必须要有一个键名为symbol.iterator的方法,这个方法是一个迭代器工厂,调用它必需返回一个迭代器对象。像这样实现了Iterable接口的,就被称为可迭代对象

4.可迭代对象与for-of的关系

答:有些原生的语言结构,可以接收可迭代对象,自动调用可迭代对象上的Iterable接口。例如,for-of就可以接受一个可迭代对象,循环调用它所关联的迭代器。

5.迭代器对象如何定义?

答:迭代器对象上必须要有一个next方法,当调用next方法时迭代器关联的可迭代对象将会被迭代一次,next方法会返回一个teratorResult对象,这个对象包含两个属性:属性value是当前迭代的值;属性done表示是否可以通过再次调用next获取下一个值。

2.自定义一个可迭代对象

根据上面所讲的基础知识,我们就可以自定义一个可迭代对象:

class Counter {
  constructor(limit) {
    this.limit = limit
  }
  [Symbol.iterator]() {
    let count = 1,
      limit = this.limit

    return {
      next() {
        if (count <= limit) {
          return { done: false, value: count++ }
        } else {
          return { done: true, value: undefined }
        }
      },
    }
  }
}

let counter = new Counter(3)
for (let i of counter) {
  console.log(i)
}
// 1
// 2
// 3

可以看到我创建了一个Counter类型,它具有[Symbol.iterator]方法 , 所以它的实例就是可迭代对象 , 可以被for-of消费。

3.迭代器也是可迭代对象?

回到开头的那个知识点,这个知识点的意思其实就是:“迭代器也是一个可迭代对象”。

const arr = [1, 2, 3]

const iter = arr[Symbol.iterator]()
console.log(iter) //Object [Array Iterator] {}

for (const i of iter) {
  console.log(i);
}
//   1
//   2
//   3

从上面的例子中就可以看到,数组就是符合这一规律的,数组所关联的迭代器是一个可迭代对象。

当我学到那个地方的时候就十分好奇我自定义的可迭代对象上的迭代器是否符合这个规律呢?于是我进行了测试

let counter = new Counter(3)

const iterator = counter[Symbol.iterator]()

for (const i of iterator) {
   console.log(i); 
}

报错信息明明白白的告诉我,我手搓的这个手工迭代器不是一个可迭代对象😅

很显然JS内置的这些迭代器 (例如数组关联的迭代器) 与我自定义的迭代器肯定是有差异的,这种差异我暂时还无法搞清楚。

二、实现自引用迭代器

1.来自生成器的灵感

之后在学习生成器的过程中 , 我看到了这样一句话:

生成器对象实现了Iterable接口,它们默认的迭代器是自引用的

同时还给出了如下示例

function* generatorFn() {}
console.log(generatorFn);
// f* generatorFn() {}
console.log(generatorFn()[Symbol.iterator]);
// f [Symbol.iterator]() {native code}
console.log(generatorFn());
// generatorFn {<suspended>}
console.log(generatorFn()[Symbol.iterator]());
// generatorFn {<suspended>}
const g = generatorFn();
console.log(g === g[Symbol.iterator]());
// true

此时我萌生了一个想法:“是不是我自定义的迭代器与JS内置迭代器的差异就是自引用”。于是我又进行了一个测试:

const arr = [1, 2, 3];

const iter = arr[Symbol.iterator]();
console.log(iter);//Object [Array Iterator] {}
const i = iter[Symbol.iterator]();
console.log(i);//Object [Array Iterator] {}

console.log(i[Symbol.iterator]());//Object [Array Iterator] {}

console.log(iter == i);//true
console.log(iter == i[Symbol.iterator]());//true

可以看到不仅数组的迭代器是可迭代对象,而其迭代器的迭代器也是可迭代对象,依次类推都是一样的。 并且这些迭代器其实都是同一个 , 这应该就是所谓的自引用 。

2.结论

基于之前的分析我最终给能够实现自引用的迭代器明确了定义,并升级了我自定义的迭代器:

自引用迭代器,是指迭代器对象上也实现了Iterable接口,并且这个接口返回的就是迭代器本身

  class Iterable {
    constructor(limit) {
      this.limit = limit;
    }

    [Symbol.]() {
      let count = 0,
        limit = this.limit;

      return {
        next() {
          if (count < limit) {
            return { value: count++, done: false };
          } else {
            return { value: undefined, done: true };
          }
        },
        [Symbol.iterator]() {
          return this;
        },
      };
    }
  }


  const iterable1 = new Iterable(5)

   const iter1 = iterable1[Symbol.iterator]()

   const iter2 = iter1[Symbol.iterator]()

   console.log(iter1 == iter2);//true

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值