手搓自引用迭代器

一、关于迭代器的问题

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

因为每个迭代器也实现了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.自定义一个可迭代对象

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

JavaScript 复制代码
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.迭代器也是可迭代对象?

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

JavaScript 复制代码
  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

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

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

JavaScript 复制代码
let counter = new Counter(3)

const iterator = counter[Symbol.iterator]()

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

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

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

二、实现自引用迭代器

1.来自生成器的灵感

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

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

同时还给出了如下示例

JavaScript 复制代码
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内置迭代器的差异就是自引用"。于是我又进行了一个测试:

JavaScript 复制代码
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接口,并且这个接口返回的就是迭代器本身

JavaScript 复制代码
  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
相关推荐
非凡ghost17 分钟前
Typora(跨平台MarkDown编辑器) v1.12.2 中文绿色版
前端·windows·智能手机·编辑器·软件需求
馨谙33 分钟前
/dev/null 是什么,有什么用途?
前端·chrome
JamSlade1 小时前
流式响应 sse 系统全流程 react + fastapi为例子
前端·react.js·fastapi
徐同保1 小时前
react useState ts定义类型
前端·react.js·前端框架
liangshanbo12152 小时前
React 19 vs React 18全面对比
前端·javascript·react.js
望获linux2 小时前
【实时Linux实战系列】Linux 内核的实时组调度(Real-Time Group Scheduling)
java·linux·服务器·前端·数据库·人工智能·深度学习
Never_Satisfied2 小时前
在 JavaScript 中,删除数组中内容为xxx的元素
java·前端·javascript
_菜鸟果果2 小时前
Vue3+echarts 3d饼图
前端·javascript·echarts
rechol3 小时前
类与对象(中)笔记整理
java·javascript·笔记
Luffe船长3 小时前
前端vue2+js+springboot实现excle导入优化
前端·javascript·spring boot