可迭代对象≠数组,一起来揭开for...of背后隐藏的秘密吧

你知道可迭代对象是什么吗?

就是可以被for...of语法遍历的对象。

那你知道for...of语法是如何遍历可迭代对象的吗?

通过数组索引?当然不是!今天,我们就一起来揭开可迭代对象的神秘面纱。

可迭代对象

一个对象要想成为可迭代对象 ,就必须实现一个[Symbol.iterator]()方法,它是一个无参数的函数,并且返回一个符合迭代器协议的对象。

js 复制代码
const customIterable = {
  [Symbol.iterator]() {
    return 符合迭代器协议的对象;
  }
};

那么,符合迭代器协议的对象又是什么?

首先,它也是一个对象。同时,它必须实现一个next()方法,在next()方法中必须返回一个符合IteratorResult接口的对象。

ts 复制代码
interface IteratorResult<T> {
  value: T;
  done: boolean;
}

现在,我们来实现一个完整的可迭代对象的例子。

迭代输出110的值。

js 复制代码
const customIterable = {
  [Symbol.iterator]() {
    let i = 0;
    return {
      next() {
        i++;
        if (i > 10) {
          return { done: true, value: undefined };
        }
        return { done: false, value: i };
      }
    };
  }
};

现在,你可以复制上面的代码,到 JS 执行环境用for...of语法去遍历,可以输出110的值。

js 复制代码
for (const myVal of customIterable) {
  console.log(myVal);
}

// 打印输出 1 2 3 4 5 6 7 8 9 10

迭代执行过程

现在,我们再根据for...of的输出结果,看看它是如何遍历可迭代对象customIterable的。

首先它调用了对象customIterable[Symbol.iterator]()方法,执行后返回了一个迭代器对象

然后循环调用迭代器对象的next()方法,获取value的值,直到donetrue的时候,停止循环。

js 复制代码
// 执行迭代器函数
const iteratorObj = customIterable[Symbol.iterator]();

// 循环调用next方法
while(true) {
  const result = iteratorObj.next();
  if (result.done) break;
  console.log(result.value);
}

如果你认真看完了上面的介绍,相信你已经明白了for...of语法的迭代原理。

所以,我们经常会用for...of去遍历数组对象,那么Array肯定也实现了一个[Symbol.iterator]()的方法。

去控制台打印Array.prototype可以看到:

当然,除了Array,常见的内置可迭代对象还有这些:

  • Map
  • Set
  • String
  • TypeArrayInt8ArrayInt16Array
  • NodeList

不止for...of,还有很多常用的可迭代对象的使用场景,比如我们最常用的数组解构语法

js 复制代码
const [a, b, ...c] = customIterable;

// a = 1
// b = 2
// c = [3, 4, 5, 6, 7, 8, 9, 10]

还有,我们常用的Promise.all方法:

js 复制代码
const result = await Promise.all(customIterable);
// result = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// 其他方法
new Set(customIterable);
Array.from(customIterable);

因此,我们不能将可迭代对象只停留在数组的层面,我们也需要了解其背后真实的原理。

扩展一下

我们上面介绍的迭代器对象,看起来有点似曾相识的感觉。

我们直接看下面的代码:

js 复制代码
function* myGenerator() {
  let i = 1;
  while(i <= 10) {
    yield i++;
  }
}

const iteratorObj = myGenerator();

while(true) {
  const result = iteratorObj.next();
  if (result.done) break;
  console.log(result.value);
}

你可以复制上面的代码,到 JS 控制台执行,同样会输出110的值。

代码中的myGenerator函数我们叫做生成器 ,它返回的其实也是一个迭代器对象 ,它与我们上面手动定义的[Symbol.iterator]()方法一样,它是 JS 的原生语法。因此,我们的可迭代对象也可以这样改写:

js 复制代码
const customIterable = {
  *[Symbol.iterator]() {
    let i = 1;
    while(i <= 10) {
      yield i++;
    }
  }
};

最后

总结一下,可迭代对象 就是实现了[Symbol.iterator]()方法的对象,并且要符合迭代协议 规范,他可以用于for...ofnew Set()以及解构语法等多种场景中。

我们不应该简单的将可迭代对象与数组等价,数组本身也是 JS 内置的一种可迭代对象。

相关推荐
JieE2129 分钟前
LeetCode 226. 翻转二叉树|JS 递归超详细拆解,二叉树入门经典题
javascript·算法
JieE21226 分钟前
LeetCode 104. 二叉树的最大深度|递归思路超详细拆解
javascript·算法
爱勇宝1 小时前
鸿蒙生态的下半场:开发者不只要能开发,还要能赚钱
android·前端·程序员
IT_陈寒4 小时前
SpringBoot这个自动配置坑我跳了三次
前端·人工智能·后端
kyriewen4 小时前
我用 AI 一周写完了整个项目,上线第一天就崩了——这是我踩过最贵的 5 个坑
前端·javascript·ai编程
Larcher5 小时前
AI Loop:让AI像人一样自主完成任务的核心机制
javascript·人工智能·设计模式
默_笙5 小时前
🃏 JS 只有 8 种数据类型,但我花了 2 天才搞懂 null 和 undefined 的区别
javascript
牧艺5 小时前
从零到协同:构建类飞书在线文档系统的五个技术重难点
前端·人工智能
jump_jump5 小时前
流式 HTML:从 htmx 片段装配到浏览器原生增量渲染
javascript·性能优化·前端工程化
红尘散仙5 小时前
想写一个像样的终端 App?试试把 React 的开发体验搬进 Rust TUI
前端·rust