可迭代对象≠数组,一起来揭开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 内置的一种可迭代对象。

相关推荐
冴羽1 小时前
今日苹果 App Store 前端源码泄露,赶紧 fork 一份看看
前端·javascript·typescript
蒜香拿铁1 小时前
Angular【router路由】
前端·javascript·angular.js
brzhang1 小时前
读懂 MiniMax Agent 的设计逻辑,然后我复刻了一个MiniMax Agent
前端·后端·架构
西洼工作室1 小时前
高效管理搜索历史:Vue持久化实践
前端·javascript·vue.js
广州华水科技1 小时前
北斗形变监测传感器在水库安全中的应用及技术优势分析
前端
樱花开了几轉2 小时前
element ui下拉框踩坑
开发语言·javascript·ui
开发者如是说2 小时前
Compose 开发桌面程序的一些问题
前端·架构
故事不长丨2 小时前
【Java SpringBoot+Vue 实现视频文件上传与存储】
java·javascript·spring boot·vscode·后端·vue·intellij-idea
旺代2 小时前
Token 存储与安全防护
前端
洋不写bug3 小时前
html实现简历信息填写界面
前端·html