ES6:【深扒】 JavaScript 中的迭代器

2. Iterator 的工作原理

  1. 创建一个指针对象,指向当前数据结构的起始位置

  2. 第一次调用next方法时,指针指向数据结构的第一个成员

  3. 接下来调用next方法,指针后移,直到指向最后一个成员

每次调用 next 方法都会返回一个值,这个值是一个 object,包含两个属性,valuedonevalue表示具体的返回值,done 是布尔类型的,表示集合是否遍历完成,完成则返回 true,否则返回 false

3. 手写实现 iterator 接口

function myIterator(arr) { let nextIndex = 0; return { next: function () { return nextIndex < arr.length ? { value: arr[nextIndex++], done: false } : { value: undefined, done: true } } } } let arr = [1,2,'ljc'] let it = myIterator(arr) console.log(it.next()); console.log(it.next()); console.log(it.next()); console.log(it.next()); console.log(it.next());

image-20210808230418267

我们来分析一下上面的代码,myIterator 函数是一个遍历器生成函数,它的作用是返回一个遍历器对象,也是指针对象。

我们通过 next 方法来移动指针,next 方法内部通过闭包来保存指针nextIndex的值,每次调用 next 方法 nextIndex都会 +1 ,然后根据nextIndex 的值从数组内取出数据作为 value ,通过索引判断得到 done ,当无数据可用时,超过数组最大索引,无可用数据返回,此时 donetrue

可迭代对象


了解过了 iterator,并且我们也已经知道了如何创建一个遍历器对象,但是这和我们先前所说的 for...of 循环有什么关系呢

这里首先我们需要了解一下 for...of运行机制

for...of循环执行时,循环内部会自动调用这个对象上的迭代器方法Symbol.iterator , 依次执行迭代器对象的 next 方法,将 next 方法的返回值赋值给 for ...of 内的变量,从而得到具体的值,实现遍历。

1. 手写实现可迭代对象

一个数据结构只要具有 Symbol.iterator 属性,就可以认为是“可遍历的”。

Symbol.iterator 属性本身是一个函数,就是当前数据结构默认的遍历器生成函数,执行这个函数,就会返回一个迭代器对象

也就是说要实现可迭代对象只要在对象上部署Symbol.iterator属性,为它创建一个迭代器方法就可以了

let iteratorObj = { items: [1, 2, 'ljc'], // 部署Symbol.iterator属性 [Symbol.iterator]: function () { let self = this let i = 0 return { next: function () { // 类型转化为Boolean let done = (i >= self.items.length) // 数据确认 let value = !done ? self.items[i++] : undefined // 数据返回 return { done, value } } } } } for (let item of iteratorObj) { console.log(item); // 1 2 ljc }

显然实现了 iterator 接口,可以被 for...of 成功遍历

2. Iterator 原生应用场景

有些对象我们并没有为它们部署 Iterator 接口,但是仍然可以使用 for...of 进行遍历。这是因为在ES6中有些对象已经默认部署了这个接口。

原生具备 Iterator 接口的数据结构:

  • Array

  • set容器

  • map容器

  • String

  • 函数的 arguments 对象

  • NodeList 对象

Array

在数组上成功的找到了 Symbol.iterator 方法,并能够执行返回迭代器对象,同时验证了for...of循环成功执行

let arr = [1, 2, 3] let it = arr[Symbol.iterator]()//返回迭代器对象 console.log(it.next()); console.log(it.next()); console.log(it.next()); console.log(it.next()); for (let item of arr) { console.log(item); }

image-20210809001450172

其余几种都一个套路,不多说

Q&A

看到这里你可能会想,为什么这么多数据结构都实现了默认部署,为什么偏偏对象没有呢?

当然是有原因的

对象可能有各种各样的属性,不像数组的值是有序的,所以对对象遍历时根本不知道如何确定先后顺序,所以需要我们手动实现

提前退出循环


普通的 for 循环是可以随时中断的,for...of 循环作为 forforEach 的升级版同样是可以的

迭代器对象除了有 next 方法,还有两个可选方法 return 方法和 throw 方法

return 方法的使用场景是,当 for...of 循环提前退出,就会调用 return 方法。

需要特别注意的是,return 方法必须有一个 object 类型的返回值

我们在前面代码的基础上添加上 return 方法,并在 for...of 循环中采用 break 语句来中断循环,循环提前退出,自动调用 return 方法输出提前退出

let iteratorObj = { items: [1, 2, 'ljc'], // 部署Symbol.iterator属性 [Symbol.iterator]: function () { let self = this let i = 0 return { next: function () { // 类型转化为Boolean let done = (i >= self.items.length) // 数据确认 let value = !done ? self.items[i++] : undefined // 数据返回 return { done, value } }, return () { console.log('提前退出'); return { done: true } } } } } for (let item of iteratorObj) { console.log(item); // 1 break; }

注意

如果采用抛出异常的方式退出,会先执行 return 方法再抛出异常

关于 throw 方法会在下篇生成器文章中提到

Iterator 接口使用场景


除了 for...of 循环会自动调用 iterator 接口之外,还有几个场景也会自动调用

1. 解构赋值

对可迭代对象进行解构赋值时,会默认调用 Symbol.iterator 方法

let map = new Set().add('a').add('b'); let [x, y] = map console.log(x, y, map) // a b Set(2) {"a", "b"}

由于解构赋值适用于可迭代对象,那么我们对自己自定义的可迭代对象解构赋值试试

let iteratorObj = { items: [1, 2, 'ljc'], // 部署Symbol.iterator属性 [Symbol.iterator]: function () { let self = this let i = 0 return { next: function () { // 类型转化为Boolean let done = (i >= self.items.length) // 数据确认 let value = !done ? self.items[i++] : undefined // 数据返回 return { done, value } } } } } let [a, b, c] = iteratorObj console.log(a, b, c)// 1 2 'ljc'

成功的实现了解构赋值

2. 扩展运算符

扩展运算符也会默认调用Symbol.iterator方法,可以将当前数据结构转化为数组

// 阮老师的例子 var str = 'hello'; [...str] // ['h','e','l','l','o'] let arr = ['b', 'c']; ['a', ...arr, 'd'] // ['a', 'b', 'c', 'd']

3. yield*

yield*后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。

let generator = function* () { yield 1; yield* [2,3,4]; yield 5; }

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

核心竞争力,怎么才能提高呢?

成年人想要改变生活,逆转状态?那就开始学习吧~

万事开头难,但是程序员这一条路坚持几年后发展空间还是非常大的,一切重在坚持。

为了帮助大家更好更高效的准备面试,特别整理了《前端工程师面试手册》电子稿文件。

前端面试题汇总

JavaScript

性能

linux

前端资料汇总

完整版PDF资料免费分享,只需你点赞支持,动动手指点击此处就可领取了

前端工程师岗位缺口一直很大,符合岗位要求的人越来越少,所以学习前端的小伙伴要注意了,一定要把技能学到扎实,做有含金量的项目,这样在找工作的时候无论遇到什么情况,问题都不会大。

40cdce3b4.png)

linux

前端资料汇总

完整版PDF资料免费分享,只需你点赞支持,动动手指点击此处就可领取了

前端工程师岗位缺口一直很大,符合岗位要求的人越来越少,所以学习前端的小伙伴要注意了,一定要把技能学到扎实,做有含金量的项目,这样在找工作的时候无论遇到什么情况,问题都不会大。

  • 13
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值