大家好,今天我们来聊聊 JavaScript 迭代器。别急着关掉页面,保证你看完不仅能学会迭代器,还能收获意料之外的知识
前言
你是否曾经在 for-of 循环前一脸懵逼?是否在面试官问你"迭代器协议"时只会尴尬微笑?今天这篇文章,带你从原理到实战,从协议到骚操作,彻底掌握迭代器!
1. 什么是迭代器?
迭代器就是一个能帮你"一个一个"拿数据的小工具。比如你有一堆糖果,迭代器就像一个自动分糖机,每次给你一个,直到分完。
在 JavaScript 里,迭代器是实现了 next()
方法的对象,每次调用 next()
,它会返回一个形如 { value, done }
的对象。
2. for-of 的底层原理
for-of 能遍历数组、字符串、Map、Set......但它的本质其实是:
- 先获取对象的
Symbol.iterator
属性 - 调用它得到一个迭代器对象
- 不断调用迭代器的
next()
方法,直到done: true
javascript
const arr = [1, 2, 3, 4, 5]
simpleForOf(arr, (item) => {
console.log(item);
}) // 1, 2, 3, 4, 5
function simpleForOf(iterable, callback) {
const iterator = iterable[Symbol.iterator]()
while (true) {
const {value, done}
// console.log(value);
callback(value) = iterator.next() // {value: 1, done: false}
if (done) {
break
}
}
}
代码解析
simpleForOf
就是 for-of 的底层实现!- 通过
Symbol.iterator
拿到迭代器,然后不断next()
。 - 每次拿到一个值,执行回调。
这就是 for-of 的灵魂!
3. 迭代器协议和 Symbol.iterator
markdown
# for of
- 只能遍历拥有迭代器属性的对象
- 原理
# 迭代器属性
[Symbol.iterator]: function() {}
# 迭代器协议
重点
- 只有实现了
[Symbol.iterator]
方法的对象才能被 for-of 遍历。 - 迭代器协议要求对象有一个
next()
方法,每次返回{ value, done }
。
4. 手写迭代器,骚操作一箩筐
手写一个迭代器:
javascript
function createIterator(arr) {
let index = 0
return {
next: function() {
if (index < arr.length) {
return {value: arr[index++], done: false}
}
return {value: undefined, done: true}
}
}
}
const myIterator = createIterator([1, 2, 3])
console.log(myIterator.next()); // { value: 1, done: false }
console.log(myIterator.next()); // { value: 2, done: false }
console.log(myIterator.next()); // { value: 3, done: false }
console.log(myIterator.next()); // { value: undefined, done: true }
代码解析
createIterator
返回一个有next()
方法的对象。- 每次调用
next()
,返回当前值和是否结束。 - 这就是迭代器协议的标准实现!
手写迭代器,面试官看了都要点赞!👍
5. 对象也能 for-of?
正常情况下,对象不能 for-of,因为没有实现 [Symbol.iterator]
。但你在自己给它加一个迭代器, 看我如何玩出花:
方案一:原型上加迭代器
javascript
Object.prototype[Symbol.iterator] = function*() {
return yield* Object.values(this)
}
let [a, b] = {a: 1, b: 2}
console.log(a, b)
代码解析
- 给所有对象加上
[Symbol.iterator]
,让它们可以 for-of! - 用生成器
function*
,直接 yield 出所有值。 - 这样就能解构对象了!
方案二:对象自定义迭代器
javascript
let obj = {
a: 1,
b: 2,
c: 3
}
obj[Symbol.iterator] = function() {
let index = 0
let keys = Object.keys(this)
return {
next: () => {
if (index < keys.length) {
return {value: this[keys[index++]], done: false}
}
return {value: undefined, done: true}
}
}
}
代码解析
- 给单个对象加迭代器属性。
- 遍历对象的 key,每次返回对应的 value。
这样对象也能 for-of,骚操作 get!😎
方案三: 借用数组的迭代器
js
Object.prototype[Symbol.iterator] = function() {
return Object.values(this)[Symbol.iterator]()
}
代码解析
- 直接用Object.values方法获取到对象上的所有属性值返回一个数组
- 让对象的迭代器等于对象值数组的迭代器
让数组的迭代器顶替对象的迭代器,一手狸猫换太子😎
6. for-in 和 for-of 的区别
javascript
let arr = [1, 2, 3]
for (let index in arr) {
console.log(index, arr[index])
}
代码解析
for-in
遍历的是索引(key),而不是值。for-of
遍历的是值。
对象的 for-in
javascript
let obj = {
a: 1,
b: 2,
c: 3
}
for (let key in obj) { // 可以遍历到对象原型上的属性
console.log(key, obj[key])
}
for-in 会遍历原型上的属性,for-of 只遍历迭代器返回的值。
7. 总结与面试技巧
面试官问你 for-of 原理?
- 说出 Symbol.iterator,迭代器协议,next 方法,done 属性。
- 能手写一个迭代器,面试官直接给你 Offer!
for-in 和 for-of 的区别?
- for-in 遍历 key,for-of 遍历 value。
- for-in 能遍历原型链,for-of 只遍历迭代器返回的内容。
对象能不能 for-of?
- 默认不能,但可以自己加 Symbol.iterator。
- 还能用生成器让对象支持解构。
结语
迭代器其实很简单,只要你敢动手写一遍,面试、项目都能用得上!
最后送你一句话:
"会写迭代器的人,代码都不会太差!"
祝你面试顺利,代码越写越骚!