我将创建一个小知识点系列文章,每篇文章只围绕一个很小的知识点展开,阅读成本低,压力小,目的是能够以比较少的时间对一个知识点进行比较扎实的理解和掌握。
在 JavaScript 中,扩展运算符(...)的核心含义是 “将集合(数组或对象)中的元素打破原集合的‘包裹性’并扩展到新的上下文环境中”,这也是它被称为 “扩展运算符” 的原因 ——它的作用是把原集合的内容 “扩展”(展开)到新的结构里。
这种 “扩展” 行为让我们可以更简洁地实现数组/对象的复制、合并等操作,而无需手动遍历元素。
但是需要注意这种对数组/对象的复制和合并行为是浅拷贝的,也就是说如果数组的元素或者对象的属性是引用数据类型,那么复制的是引用地址,当原数据变化的时候,复制或者合并生成的数据也会发生对应的变化。
一、数组
对于数组,扩展的是数组中的每一个元素。
数组是用 [] 包裹的元素集合,... 把元素从 [] 中 “扩展” 出来,不再受原数组的结构限制,并插入到新的数组结构中。
1、复制数组
通过扩展运算符可以快速创建原数组的副本。
const a1 = [1, 2];
const a2 = [...a1];
// a2 [1, 2] 与 a1 完全独立
2、合并数组
无需 concat 方法,直接拼接多个数组.
const a1 = ['a', 'b'];
const a2 = ['c'];
const a3 = ['d', 'e'];
[...a1, ...a2, ...a3]
// ['a', 'b', 'c', 'd', 'e']
3、函数参数中展开
扩展运算符还能用于函数调用,将数组元素展开为用逗号分隔的参数序列。
const nums = [1, 2, 3];
Math.max(...nums); // 3(等价于 Math.max(1, 2, 3))
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1.push(...arr2);
4、与解构赋值结合
扩展运算符可以与解构赋值结合起来,用于生成数组,并且只能放在参数的最后一位。
const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest // [2, 3, 4, 5]
二、对象
对于对象,扩展的是对象中的每一个键值对。
对象是用 {} 包裹的键值对集合,... 把键值对从 {} 中 “扩展” 出来,允许它们被重新组合,合并到新的对象中。
1、复制对象
快速创建原对象的浅拷贝副本。
let m = { a: 3, b: 4 };
let n = { ...m };
// n {a: 3, b: 4}
2、合并对象
合并多个对象的键值对,同名属性后面覆盖前面。
let m = { a: 3, b: 4 };
let n = { b: 5, c: 6 };
let mn = { ...m, ...n };
// mn {a: 3, b: 5, c: 6}
3、修改或添加属性
如果用户自定义的属性,放在扩展运算符后面,则扩展运算符内部的同名属性会被覆盖掉。
let m = { a: 3, b: 4 };
let n = {
...m,
a: 5, // 覆盖原属性 a
c: 6 // 添加新属性 c
};
// n {a: 5, b: 4, c: 6}
4、设置对象的默认值
如果把自定义属性放在扩展运算符前面,就变成了设置新对象的默认属性值。
let m = { a: 3 };
let n = { a: 1, b: 2, ...m };
// n {a: 3, b: 2}
5、与解构赋值结合
扩展运算符可以与解构赋值结合起来,对象的解构赋值用于从一个对象取值,尚未被读取的属性会分配到扩展运算符(...)跟随的对象上面,并且该对象必须是最后一个参数。
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }
附加:与Object.assign比较
虽然如上对对象的复制、合并和扩展操作也都可以通过Object.assign方法实现,但推荐使用扩展运算符,原因如下:
- 语法更简洁直观
// 使用 Object.assign
const merged1 = Object.assign({}, obj1, obj2, obj3);
// 使用扩展运算符 - 更简洁
const merged2 = { ...obj1, ...obj2, ...obj3 };
- 可以避免意外修改原对象
Object.assign 第一个参数若为原对象,会直接修改它;扩展运算符始终返回新对象。
const original = { x: 1, y: 2 };
const wrong = Object.assign(original, { z: 3 }); // 修改了 original!
const correct = { ...original, z: 3 }; // original 不变
- 条件属性添加更方便
结合逻辑运算符,轻松实现 “满足条件才添加属性”。
const condition = true;
const baseObj = { name: 'test' };
// 扩展运算符支持条件属性
const result = {
...baseObj,
...(condition && { extra: 'value' }),
age: 25
};
// Object.assign 实现相同功能更复杂
const result2 = Object.assign(
{},
baseObj,
condition ? { extra: 'value' } : {},
{ age: 25 }
);
- 与数组操作的一致性
数组和对象都用 ... 扩展,减少记忆成本。
- 性能考虑
虽然在大多数情况下性能差异不大,但扩展运算符在现代环境中通常有更好的性能表现。