Skip to content

Iterator 迭代器

Iterator(迭代器)是 ES6 引入的一种统一的遍历机制,为各种数据结构提供了统一的访问接口。

迭代器协议#

什么是迭代器#

迭代器是一个对象,它有一个 next() 方法,每次调用返回 { value, done }

// 手动创建一个迭代器
function createIterator(items) {
let index = 0
return {
next() {
if (index < items.length) {
return { value: items[index++], done: false }
}
return { value: undefined, done: true }
},
}
}
const iterator = createIterator([1, 2, 3])
iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }
iterator.next() // { value: 3, done: false }
iterator.next() // { value: undefined, done: true }

可迭代协议#

可迭代对象需要实现 [Symbol.iterator] 方法,该方法返回一个迭代器:

const iterable = {
data: [1, 2, 3],
[Symbol.iterator]() {
let index = 0
const data = this.data
return {
next() {
if (index < data.length) {
return { value: data[index++], done: false }
}
return { done: true }
},
}
},
}
// 现在可以用 for...of 遍历
for (const item of iterable) {
console.log(item) // 1, 2, 3
}
// 也可以用扩展运算符
;[...iterable] // [1, 2, 3]

内置可迭代对象#

以下内置类型都实现了迭代器协议:

// 数组
for (const item of [1, 2, 3]) {
console.log(item)
}
// 字符串
for (const char of 'hello') {
console.log(char)
}
// Map
for (const [key, value] of new Map([
['a', 1],
['b', 2],
])) {
console.log(key, value)
}
// Set
for (const item of new Set([1, 2, 3])) {
console.log(item)
}
// arguments
function test() {
for (const arg of arguments) {
console.log(arg)
}
}
// NodeList
for (const node of document.querySelectorAll('div')) {
console.log(node)
}

获取迭代器#

const arr = [1, 2, 3]
const iterator = arr[Symbol.iterator]()
iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }

自定义可迭代对象#

范围迭代器#

class Range {
constructor(start, end, step = 1) {
this.start = start
this.end = end
this.step = step
}
[Symbol.iterator]() {
let current = this.start
const { end, step } = this
return {
next() {
if (current <= end) {
const value = current
current += step
return { value, done: false }
}
return { done: true }
},
}
}
}
const range = new Range(1, 10, 2)
;[...range] // [1, 3, 5, 7, 9]
for (const n of new Range(1, 5)) {
console.log(n) // 1, 2, 3, 4, 5
}

链表迭代器#

class Node {
constructor(value) {
this.value = value
this.next = null
}
}
class LinkedList {
constructor() {
this.head = null
this.tail = null
}
append(value) {
const node = new Node(value)
if (!this.head) {
this.head = node
this.tail = node
} else {
this.tail.next = node
this.tail = node
}
return this
}
*[Symbol.iterator]() {
let current = this.head
while (current) {
yield current.value
current = current.next
}
}
}
const list = new LinkedList()
list.append(1).append(2).append(3)
;[...list] // [1, 2, 3]

树结构迭代器#

class TreeNode {
constructor(value, children = []) {
this.value = value
this.children = children
}
// 深度优先遍历
*[Symbol.iterator]() {
yield this.value
for (const child of this.children) {
yield* child
}
}
}
const tree = new TreeNode(1, [
new TreeNode(2, [new TreeNode(4), new TreeNode(5)]),
new TreeNode(3, [new TreeNode(6)]),
])
;[...tree] // [1, 2, 4, 5, 3, 6]

for…of 循环#

for...of 是消费迭代器的主要方式:

const arr = [1, 2, 3]
// for...of 遍历值
for (const item of arr) {
console.log(item) // 1, 2, 3
}
// for...in 遍历键
for (const index in arr) {
console.log(index) // '0', '1', '2'
}
// for...of 不能直接遍历普通对象
const obj = { a: 1, b: 2 }
// for (const item of obj) {} // TypeError
// 需要借助 Object.entries 等
for (const [key, value] of Object.entries(obj)) {
console.log(key, value)
}

中断迭代#

for (const item of [1, 2, 3, 4, 5]) {
if (item === 3) break
console.log(item) // 1, 2
}
for (const item of [1, 2, 3, 4, 5]) {
if (item === 3) continue
console.log(item) // 1, 2, 4, 5
}

return 方法#

迭代器可以定义 return() 方法,在循环提前退出时调用:

function createIterator() {
let index = 0
const data = [1, 2, 3, 4, 5]
return {
[Symbol.iterator]() {
return this
},
next() {
if (index < data.length) {
return { value: data[index++], done: false }
}
return { done: true }
},
return() {
console.log('Iterator closed')
return { done: true }
},
}
}
const iterator = createIterator()
for (const item of iterator) {
console.log(item)
if (item === 2) break
}
// 1
// 2
// Iterator closed

使用迭代器的场景#

扩展运算符#

const arr = [...'hello'] // ['h', 'e', 'l', 'l', 'o']
const set = new Set([...'hello']) // Set {'h', 'e', 'l', 'o'}

解构赋值#

const [a, b, c] = new Set([1, 2, 3])
console.log(a, b, c) // 1 2 3
const [first, ...rest] = 'hello'
console.log(first) // 'h'
console.log(rest) // ['e', 'l', 'l', 'o']

Array.from#

const map = new Map([
['a', 1],
['b', 2],
])
Array.from(map) // [['a', 1], ['b', 2]]
Array.from(map.keys()) // ['a', 'b']
Array.from(map.values()) // [1, 2]

Promise.all 等#

const promises = new Set([
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3),
])
Promise.all(promises).then(console.log) // [1, 2, 3]

实用工具函数#

创建范围#

function* range(start, end, step = 1) {
for (let i = start; i <= end; i += step) {
yield i
}
}
;[...range(1, 5)] // [1, 2, 3, 4, 5]
;[...range(0, 10, 2)] // [0, 2, 4, 6, 8, 10]

无限序列#

function* naturals() {
let n = 1
while (true) {
yield n++
}
}
function take(iterable, n) {
const result = []
for (const item of iterable) {
if (result.length >= n) break
result.push(item)
}
return result
}
take(naturals(), 5) // [1, 2, 3, 4, 5]

迭代器工具#

// map
function* map(iterable, fn) {
for (const item of iterable) {
yield fn(item)
}
}
// filter
function* filter(iterable, predicate) {
for (const item of iterable) {
if (predicate(item)) {
yield item
}
}
}
// take
function* take(iterable, n) {
let count = 0
for (const item of iterable) {
if (count++ >= n) break
yield item
}
}
// 组合使用
const result = [
...take(
filter(
map([1, 2, 3, 4, 5], (x) => x * 2),
(x) => x > 4
),
2
),
]
// [6, 8]

小结#

概念说明
迭代器有 next() 方法的对象
可迭代对象有 [Symbol.iterator] 方法的对象
for…of遍历可迭代对象的值
使用场景扩展运算符、解构、Array.from 等

迭代器协议为 JavaScript 提供了统一的遍历接口,是 Generator、for…of、扩展运算符等特性的基础。