扩展运算符
# 理解对象扩展运算符的行为差异
在 JavaScript 中,扩展运算符 ...
的行为会根据上下文有所不同。让我们深入分析为什么[...a]
会报错而 {...a}
却能正常工作。
# 问题分析
const a = {s:4, y:7};
// 这行会报错
[...a]; // TypeError: a is not iterable
// 这行能正常工作
let b = {...a}; // b = {s:4, y:7}
1
2
3
4
5
6
7
2
3
4
5
6
7
# 为什么 [...a]
会报错?
数组扩展运算符 [...value]
要求 value
必须是可迭代对象。普通对象默认不是可迭代的,因此会抛出错误。
# 为什么 {...a}
能工作?
对象扩展运算符 {...value}
会复制 value
的所有可枚举自有属性到一个新对象中。普通对象有可枚举属性,因此可以正常工作。
# 深入理解
# 1. 可迭代协议(Iterable Protocol)
要使对象可迭代,它必须实现 Symbol.iterator
方法:
const iterableObject = {
data: [1, 2, 3],
{
let index = 0;
return {
next: () => {
if (index < this.data.length) {
return { value: this.data[index++], done: false };
}
return { done: true };
}
};
}
};
console.log([...iterableObject]); // [1, 2, 3]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 2. 对象扩展的工作原理
对象扩展运算符会复制源对象的所有可枚举自有属性:
const obj = { a: 1, b: 2 };
const clone = { ...obj }; // { a: 1, b: 2 }
// 等同于
const manualClone = {};
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
manualClone[key] = obj[key];
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 3. 数组扩展的工作原理
数组扩展运算符要求对象实现迭代器协议:
const arrayLike = {
0: 'a',
1: 'b',
length: 2,
{
let index = 0;
return {
next: () => {
if (index < this.length) {
return { value: this[index++], done: false };
}
return { done: true };
}
};
}
};
console.log([...arrayLike]); // ['a', 'b']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 解决方案
# 1. 使对象可迭代
如果你想对普通对象使用数组扩展运算符,需要实现 Symbol.iterator
方法:
const a = {
s: 4,
y: 7,
{
const keys = Object.keys(this);
let index = 0;
return {
next: () => {
if (index < keys.length) {
const key = keys[index++];
return { value: [key, this[key]], done: false };
}
return { done: true };
}
};
}
};
console.log([...a]);
// [['s', 4], ['y', 7]]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 2. 使用 Object 方法替代
更常见的是使用 Object
方法来处理对象属性:
const a = {s:4, y:7};
// 获取键数组
console.log(Object.keys(a)); // ['s', 'y']
// 获取值数组
console.log(Object.values(a)); // [4, 7]
// 获取键值对数组
console.log(Object.entries(a)); // [['s', 4], ['y', 7]]
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 3. 对象扩展的常见用法
对象扩展运算符在多种场景下非常有用:
// 合并对象
const obj1 = { a: 1 };
const obj2 = { b: 2 };
const merged = { ...obj1, ...obj2 }; // {a:1, b:2}
// 添加新属性
const withNewProp = { ...a, z: 10 }; // {s:4, y:7, z:10}
// 覆盖属性
const overridden = { ...a, s: 100 }; // {s:100, y:7}
// 创建浅拷贝
const shallowCopy = { ...a };
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 关键区别总结
特性 | 数组扩展 [...obj] | 对象扩展 {...obj} |
---|---|---|
要求 | 对象必须可迭代 | 对象必须有可枚举属性 |
结果 | 数组 | 对象 |
用途 | 将可迭代对象转为数组 | 复制/合并对象属性 |
默认支持 | Array, String, Map, Set 等 | 所有对象 |
错误情况 | 对象不可迭代时报错 | 永远不会报错(对非对象会尝试转为对象) |
# 实际应用建议
对数组使用数组扩展:
const arr = [1, 2, 3]; const newArr = [...arr, 4]; // [1, 2, 3, 4]
1
2对对象使用对象扩展:
const obj = { a: 1, b: 2 }; const newObj = { ...obj, c: 3 }; // {a:1, b:2, c:3}
1
2需要对象键值对数组时:
const obj = { a: 1, b: 2 }; const entries = Object.entries(obj); // [['a',1], ['b',2]]
1
2
理解这些区别有助于避免常见的错误,并写出更健壮的 JavaScript 代码。
编辑 (opens new window)
上次更新: 2025/08/05, 08:02:06