手搓自引用迭代器

一、关于迭代器的问题

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

因为每个迭代器也实现了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
相关推荐
Data analyse4561 小时前
rnweb 回放数据量太大怎么办?分片存储与断点续传的实现思路
前端·网络·算法·数据分析
xiaogai_gai1 小时前
金蝶收料通知单查询策略实战教程:从源系统拉取到中间层落地的完整路径
java·前端·数据库
环境栈笔记2 小时前
指纹浏览器 WebRTC 怎么设置?从 ICE 候选、代理出口到功能验证
前端·人工智能·后端·自动化
赵大仁2 小时前
官网性能优化实战:LCP 3.2s → 2.1s、CLS 0.107 → 0,以及被我推翻的 6 个判断
前端·性能优化·cls·lcp·core web vitals
xiancai_xianyu2 小时前
Text2SQL落地生产库前,SQL要过哪三道闸
前端·数据库·sql·sql注入·text2sql·ai agent开发·企业级ai框架
小羊没烦恼!3 小时前
T-SQL查询进阶--理解SQL SERVER中的分区表
服务器·开发语言·前端·javascript·html
史瓦辛格3 小时前
给 DeepSeek Harness 写费用插件:把「这次会话花了多少钱」算准
前端·deepseek
星光不问赶路人3 小时前
Vue 3 + TypeScript 中 enum 的使用实践
前端·vue.js
鸥梨菌Honevid3 小时前
UVM 中 REQ 和 RSP 类型详解:从通信模型到 item_done 与 put_response
java·前端·servlet
IT_陈寒3 小时前
Python多进程共享变量这个坑,差点让我加班到凌晨
前端·人工智能·后端