2. Iterator 的工作原理
-
创建一个指针对象,指向当前数据结构的起始位置
-
第一次调用
next
方法时,指针指向数据结构的第一个成员 -
接下来调用
next
方法,指针后移,直到指向最后一个成员
每次调用 next
方法都会返回一个值,这个值是一个 object
,包含两个属性,value
和 done
。value
表示具体的返回值,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());
我们来分析一下上面的代码,myIterator
函数是一个遍历器生成函数,它的作用是返回一个遍历器对象,也是指针对象。
我们通过 next
方法来移动指针,next
方法内部通过闭包来保存指针nextIndex
的值,每次调用 next
方法 nextIndex
都会 +1
,然后根据nextIndex
的值从数组内取出数据作为 value
,通过索引判断得到 done
,当无数据可用时,超过数组最大索引,无可用数据返回,此时 done
为 true
了解过了 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); }
其余几种都一个套路,不多说
Q&A
看到这里你可能会想,为什么这么多数据结构都实现了默认部署,为什么偏偏对象没有呢?
当然是有原因的
对象可能有各种各样的属性,不像数组的值是有序的,所以对对象遍历时根本不知道如何确定先后顺序,所以需要我们手动实现
普通的 for
循环是可以随时中断的,for...of
循环作为 for
和 forEach
的升级版同样是可以的
迭代器对象除了有 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
方法会在下篇生成器文章中提到
除了 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前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)
![](https://i-blog.csdnimg.cn/blog_migrate/439b32d487194509eb1b2b3d5a6becde.jpeg)
核心竞争力,怎么才能提高呢?
成年人想要改变生活,逆转状态?那就开始学习吧~
万事开头难,但是程序员这一条路坚持几年后发展空间还是非常大的,一切重在坚持。
为了帮助大家更好更高效的准备面试,特别整理了《前端工程师面试手册》电子稿文件。
前端面试题汇总
JavaScript
性能
linux
前端资料汇总
完整版PDF资料免费分享,只需你点赞支持,动动手指点击此处就可领取了。
前端工程师岗位缺口一直很大,符合岗位要求的人越来越少,所以学习前端的小伙伴要注意了,一定要把技能学到扎实,做有含金量的项目,这样在找工作的时候无论遇到什么情况,问题都不会大。
40cdce3b4.png)
linux
前端资料汇总
完整版PDF资料免费分享,只需你点赞支持,动动手指点击此处就可领取了。
前端工程师岗位缺口一直很大,符合岗位要求的人越来越少,所以学习前端的小伙伴要注意了,一定要把技能学到扎实,做有含金量的项目,这样在找工作的时候无论遇到什么情况,问题都不会大。