什么是 Iterable 对象?与 Array 有什么区别?
探讨 JavaScript 中 Iterable 对象与 Array 的区别,包括定义方式、访问方式和遍历操作。通过理解这些差异,掌握如何在实际开发中选择使用。
在 JavaScript 中,Iterable 对象是指实现了 [Symbol.iterator] 方法的对象。这允许它们被迭代(如使用 for...of
循环遍历),因为该方法返回一个迭代器(Iterator)。迭代器是具有 next()
方法的对象,每次调用 next()
都返回一个形如 { value, done }
的接口值,表示当前迭代元素和是否结束。常见的 Iterable 对象包括:Array、String、Set、Map、TypedArray、NodeList,以及自定义实现[Symbol.iterator]的对象。
Array 是一种具体的集合类型,也实现了 Iterable 协议,但它们在本质和使用方式上有明显区别,主要体现在定义、访问、方法等方面。
1. 定义方式的不同
- Array: 可直接通过字面量创建,例如
[1, 2, 3]
,简洁易用。 - Iterable 对象: 需通过实现 [Symbol.iterator] 方法手动定义,例如:
const iterableObject = { [Symbol.iterator]: function* () { yield 'one'; yield 'two'; yield 'three'; } }; // 自定义迭代器通过 Generator 创建。
这表示任何对象都可遵守 Iterable 协议成为可迭代的对象,并非只有内置集合。
2. 元素访问和修改方式
- Array 的特性:
- 支持索引访问(如
arr
)和直接修改元素(如arr = 'new'
)。 - 提供数组原生方法如
push
,pop
,slice
,以便增删改元素。const arr = ['a', 'b']; console.log(arr); // 输出 'a' arr.push('c'); // 修改数组,扩展元素
- 支持索引访问(如
- Iterable 对象:
- 不可直接通过索引访问或修改元素(因为没有定义数字索引,仅通过迭代器序列读取)。
- 只支持顺序迭代访问,通常用于遍历只读序列,例如:
const iterable = iterableObject; // 来自上方示例 for (const value of iterable) { console.log(value); // 依次输出 'one', 'two', 'three' } const iter = iterable[Symbol.iterator](); console.log(iter.next().value); // 'one' - 每次调用 next() 获取下个元素。
3. 遍历和操作的适用性
- 遍历方式差异:
- Array:既可用
for...in
(遍历索引/properties,但不安全,可能访问额外属性),也能用for...of
。 - Iterable:默认只适用
for...of
循环遍历元素的 value,避免了for...in
中可能的属性意外问题。
- Array:既可用
- 操作方法:
- Array 是独立的集合类型,有丰富的内置数组方法用于数据操作(如
filter
,map
)。 - Iterable 只是协议,对象不一定有这些数组方法;如需集合操作,常需转换为 Array(如
Array.from()
),而非直接操作。
- Array 是独立的集合类型,有丰富的内置数组方法用于数据操作(如
4. 其他关键区别
- 属性扩展影响:
- Array 添加属性会影响所有遍历方式(如添加额外属性会被
for...in
检出),而 Iterable 设计专一化于序列访问。
- Array 添加属性会影响所有遍历方式(如添加额外属性会被
- 抽象层级:
- Array 是实例级对象,代表一组有序元素。
- Iterable 是更抽象的协议级概念,促进统一数据迭代机制,为 Array, Set, Map 等提供遍历能力标准化。
总结
简言之,Iterable 是 JavaScript 可迭代协议,任何对象都可遵守它成为可遍历序列;Array 则是实现了此协议的具体集合类型,提供额外操作方法。Array 是 Iterable 的一个特定实现,而 Iterable 对象并非总是类数组(如 Set)或能索引修改。实践中,为数据序列化访问优先选 Iterable;如需灵活元素的增删及索引,则依赖 Array。