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; }
这里并不是它的主战场,下节说明
在 ES6
中新增了新的数据结构,为了提供一种统一的遍历方法,新增了 for...of
方式。而 for...of
执行的时候会自动调用迭代器来取值
只有实现了 Iterator
接口的对象才能采用 for...of
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)
最后
文章到这里就结束了,如果觉得对你有帮助可以点个赞哦,如果有需要前端校招面试题PDF完整版的朋友可以点击这里即可免费获取,包括答案解析。
l7THz-1713485292579)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)
[外链图片转存中…(img-6zRvXig1-1713485292579)]
最后
文章到这里就结束了,如果觉得对你有帮助可以点个赞哦,如果有需要前端校招面试题PDF完整版的朋友可以点击这里即可免费获取,包括答案解析。
[外链图片转存中…(img-ttBnt7uH-1713485292580)]