- 宏观上来讲,迭代器的定义我们可以看出来,迭代器是 帮助我们对某个数据结构进行遍历的对象,无需关心内部实现,只需要能使用其接口和特性即可。
- 微观上来讲(JS 角度),迭代器是一个具体的对象,这个对象需要符合 迭代器协议(iterator protocol)。
- 迭代器协议定义了产生一系列值(无论是有限还是无限个)的标准方式
- 在 JS 中这个标准就是一个特定的
next
方法;
next
是一个无参或者一个参数的函数,返回一个具有两个属性(done
和 value
)的对象。
next: function() {
return {
done: boolean,
value: any
}
}
返回的对象属性:
done
:true
表示迭代结束,flase
未结束。value
: 本次迭代的值。
const colors = ['red', 'pink', 'green']
const nums = [100, 200, 300]
const iteratorFunc = arr => {
let index = 0
return {
next() {
if (index < arr.length) {
return { done: false, value: arr[index++] }
}
return { done: true, value: undefined }
}
}
}
const colorsIterator = iteratorFunc(colors)
const numsIterator = iteratorFunc(nums)
console.log(colorsIterator.next())
console.log(colorsIterator.next())
console.log(colorsIterator.next())
console.log(colorsIterator.next())
console.log(numsIterator.next())
console.log(numsIterator.next())
console.log(numsIterator.next())
console.log(numsIterator.next())
- 当一个对象实现了 iterable protocol 协议 时,它就是一个可迭代对象。
- 这个对象的要求是必须实现
@@iterator
方法,在代码中我们使用Symbol.iterator
访问该属性
- 给该对象添加
[Symbol.iterator](){}
函数(方法)。
-
可迭代对象为可迭代特性服务,也就是说,一个对象实现了可迭代性,那么他就具有一些列可迭代对象所具有的一切特性。
-
JavaScript中语法:
for ... of
、展开语法(spread syntax)、yield*
(后面讲)、解构赋值(Destructuring_assignment); -
创建一些对象时:
new Map([Iterable])
、new WeakMap([iterable])
、new Set([iterable])
、new WeakSet([iterable])
; -
一些方法的调用:
Promise.all(iterable)
、Promise.race(iterable)
、Array.from(iterable)
;
- 底层实现了可迭代方法,JS 或者就是
[Symbol.iterator](){}
,例如数组、字符串、类数组、Map
、Set
、NodeList
等。检测是否是可迭代对象:
Array.prototype[Symbol.iterator];
String.prototype[Symbol.iterator];
Map.prototype[Symbol.iterator];
Set.prototype[Symbol.iterator];
[!TIP] 一般对象没有实现
[Symbol.iterator]()
,但 ES9 底层做了处理,让对象也支持解构赋值和扩展运算符。
const iterableObj = {
colors: ['red', 'green', 'orange'],
[Symbol.iterator]() {
let index = 0
return {
next: () => {
if (index < this.colors.length) {
return { done: false, value: this.colors[index++] }
}
return { done: true, value: undefined }
}
}
}
}
// 1、let ... of ...
for (let color of iterableObj) {
console.log(color)
}
// 2、spread
console.log([...iterableObj])
// 3、distructing assignment
const [red] = iterableObj
console.log(red)
// 4、new Set
console.log(new Set(iterableObj))
// 5、Array.from
console.log(Array.from(iterableObj))
// 6、Promise.all()
Promise.all(iterableObj).then(res => {
console.log(res)
})
- 生成器是一个函数,能让我们更加方便的控制函数的暂停、继续执行。
- 生成器是一种特殊的迭代器,一般可以替代迭代器,简化代码
- 生成器的返回值还是一个生成器(Generator)
// 生成器是一种特殊的迭代器,所以可以代替迭代器
function* createIterableArray(arr) {
// 1、写法1
// for (const e of arr) {
// yield e
// }
// 2、写法2
// yield arr[0]
// yield arr[1]
// yield arr[2]
// 3、写法3
yield* arr
}
const names = ['peter', 'jack', 'julia']
const namesIterator = createIterableArray(names)
for (let item of namesIterator) {
console.log(item)
}
在 async/await
出现之前,回调地狱问题的解决是靠生成器解决的。 async/await
的原理就是生成器。
模拟请求:
// request.js
function requestData(url) {
// 异步请求的代码会被放入到executor中
return new Promise((resolve, reject) => {
// 模拟网络请求
setTimeout(() => {
// 拿到请求的结果
resolve(url)
}, 2000);
})
}
需求:
- url: why -> res: why
- url: res + "aaa" -> res: whyaaa
- url: res + "bbb" => res: whyaaabbb
- 第一种方案: 多次回调
// 回调地狱
requestData("why").then(res => {
requestData(res + "aaa").then(res => {
requestData(res + "bbb").then(res => {
console.log(res)
})
})
})
- 第二种方案: Promise中then的返回值来解决
requestData("why").then(res => {
return requestData(res + "aaa")
}).then(res => {
return requestData(res + "bbb")
}).then(res => {
console.log(res)
})
- 第三种方案: Promise + generator实现
function* getData() {
const res1 = yield requestData("why")
const res2 = yield requestData(res1 + "aaa")
const res3 = yield requestData(res2 + "bbb")
const res4 = yield requestData(res3 + "ccc")
console.log(res4)
}
// 1> 手动执行生成器函数
// const generator = getData()
// generator.next().value.then(res => {
// generator.next(res).value.then(res => {
// generator.next(res).value.then(res => {
// generator.next(res)
// })
// })
// })
// 2> 自己封装了一个自动执行的函数
function execGenerator(genFn) {
const generator = genFn()
function exec(res) {
const result = generator.next(res)
if (result.done) {
return result.value
}
result.value.then(res => {
exec(res)
})
}
exec()
}
execGenerator(getData)
- 第四种方案: async/await
async function getData() {
const res1 = await requestData("why")
const res2 = await requestData(res1 + "aaa")
const res3 = await requestData(res2 + "bbb")
const res4 = await requestData(res3 + "ccc")
console.log(res4)
}
getData()
实现以下效果:
>>> [...5]
[1, 2, 3, 4, 5]