处理集合中的每个项是很常见的操作。JavaScript 提供了许多迭代集合的方法,从简单的
for
循环到map()
和filter()
。迭代器和生成器将迭代的概念直接带入核心语言,并提供了一种机制来自定义
for...of
循环的行为。
🌰迭代器练习:
const students = ["lisa", "mike", "beak"]let index = 0
const studentsIterator = {next: function() {if(index < students.length) {return {done: false, value: students[index++] }} else {return { done: true, value: undefined }}}
}console.log(studentsIterator.next())
console.log(studentsIterator.next())
console.log(studentsIterator.next())
console.log(studentsIterator.next())
输出如下:
上述代码的弊端:我们获取一个数组的时候,需要自己创建一个index变量,再创建一个所谓的迭代器对象;
事实上我们可以对上面的代码进行进一步的封装,让其变成一个可迭代对象;
若一个对象拥有迭代行为,比如在 for...of
中会循环哪些值,那么那个对象便是一个可迭代对象。一些内置类型,如 Array
或 Map
拥有默认的迭代行为,而其他类型(比如Object
)则没有。
为了实现可迭代,一个对象必须实现 @@iterator 方法,这意味着这个对象(或其原型链中的任意一个对象)必须具有一个带 Symbol.iterator
键(key)的属性。
可以多次迭代一个迭代器,或者只迭代一次。
只能迭代一次的 Iterables(例如 Generators)通常从它们的**@@iterator方法中返回它本身,其中那些可以多次迭代的方法必须在每次调用@@iterator**时返回一个新的迭代器。
举个栗子:将Object
变成可迭代对象
// 将info变成可迭代对象/*1.必须实现一个特定的函数: [Symbol.iterator]2.这个函数需要返回一个迭代器(这个迭代器用于迭代当前的对象)*/const info = {students: ["lisa", "beak", "chen"],[Symbol.iterator]: function () {let index = 0const infoIterator = {next: function () {if (index < info.students.length) {return { done: false, value: this.students[index++] }} else {return { done: true, value: undefined }}}}return infoIterator}
}// 给info创建一个迭代器, 迭代info中的students
console.log(infoIterator.next())
console.log(infoIterator.next())
console.log(infoIterator.next())
console.log(infoIterator.next())// 可迭代对象必然具备下面的特点
const iterator = infos[Symbol.iterator]()
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())// 可迭对象可以进行for of操作
for (const item of infos) {console.log(item)
}
应用场景:
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)
虽然自定义的迭代器是一个有用的工具,但由于需要显式地维护其内部状态,因此需要谨慎地创建。
生成器函数提供了一个强大的选择:
- 它允许你定义一个包含自有迭代算法的函数
- 同时它可以自动维护自己的状态
举个栗子:
// 1.定义了一个生成器函数
function* fn() {console.log("1")console.log("2")yieldconsole.log("3")console.log("4")yieldconsole.log("5")console.log("6")
}// 2.调用生成器函数, 返回一个 生成器对象
const generator = fn()// 调用next方法
generator.next()
generator.next()
generator.next()
既然生成器是一种特殊的迭代器,那么在某些情况下我们可以使用生成器来替代迭代器:
const students = ["lisa", "mike", "beak"]function* arrayIterator(arr) {for (const item of arr) {yield item}
}
const studentsIterator = arrayIterator(students)console.log(studentsIterator.next())
console.log(studentsIterator.next())
console.log(studentsIterator.next())
console.log(studentsIterator.next())