ES6(ECMAScript 2015)引入的扩展运算符(Spread Operator),使用三个点 ...
表示,是一种非常强大且常用的语法特性。它的核心作用是将一个可迭代对象(如数组、字符串、类数组对象)或对象“展开”(展开其元素或属性),以便在需要多个参数、元素或属性的地方使用。
注意:扩展运算符
...
与 Rest 参数...
语法相同,但作用相反:
- 扩展运算符:在调用/使用时,将一个集合“打散”成单个元素。
- Rest 参数:在定义时,将多个单个元素“收集”成一个集合。
一、 扩展运算符的主要作用
- 展开数组元素:将数组的元素逐个取出。
- 展开对象属性:将对象的可枚举属性(不包括原型链上的)逐个取出。
- 展开字符串:将字符串的每个字符逐个取出。
- 展开类数组对象:如
arguments
、NodeList
等。
二、 主要使用场景
1. 数组合并与复制 (Array Concatenation & Copying)
这是最常见的用途之一。
- 合并数组:
const arr1 = [1, 2]; const arr2 = [3, 4]; const merged = [...arr1, ...arr2]; // [1, 2, 3, 4] // 等价于 arr1.concat(arr2),但更简洁
- 复制数组(浅拷贝):
注意:这是浅拷贝。如果数组元素是对象或数组,修改嵌套对象会影响原对象。const original = [1, 2, 3];
const copy = [...original]; // [1, 2, 3]
// 修改 copy 不会影响 original
copy.push(4);
console.log(original); // [1, 2, 3]
console.log(copy); // [1, 2, 3, 4] - 在任意位置插入元素:
const numbers = [1, 4, 5]; const withInsert = [0, ...numbers, 6]; // [0, 1, 4, 5, 6] const withInsert2 = [1, ...numbers.slice(1)]; // [1, 4, 5] (替换第一个元素)
2. 传递数组元素作为函数参数 (Function Arguments)
将数组的元素“打散”作为函数的独立参数传入。
function add(a, b, c) {
return a + b + c;
}
const args = [1, 2, 3];
// ES5 方式:使用 apply
console.log(add.apply(null, args)); // 6
// ES6 方式:使用扩展运算符
console.log(add(...args)); // 6 (更简洁直观)
// 也可以与其他参数结合
console.log(add(...[1, 2], 3)); // 6
3. 对象合并与复制 (Object Merging & Copying) (ES2018)
扩展运算符也可用于对象(严格来说是 ES2018 的特性,但常与 ES6 特性一起讨论)。
- 合并对象(浅拷贝):
const defaults = { theme: 'light', fontSize: 14 }; const userSettings = { theme: 'dark', color: 'blue' }; const settings = { ...defaults, ...userSettings }; console.log(settings); // { theme: 'dark', fontSize: 14, color: 'blue' } // 注意:后出现的属性会覆盖前面同名的属性
- 复制对象(浅拷贝):
注意:同样是浅拷贝。const originalObj = { name: 'Alice', age: 30 }; const copyObj = { ...originalObj }; copyObj.age = 31; console.log(originalObj.age); // 30 console.log(copyObj.age); // 31
- 添加或修改属性:
const user = { name: 'Bob' }; const updatedUser = { ...user, age: 25, name: 'Robert' }; console.log(updatedUser); // { name: 'Robert', age: 25 }
4. 将类数组对象转换为数组
将 arguments
、NodeList
、HTMLCollection
等类数组对象转换为真正的数组。
// 在函数中处理 arguments
function logArgs() {
const argsArray = [...arguments]; // 转换为数组
argsArray.forEach(arg => console.log(arg));
}
logArgs('a', 'b', 'c'); // 输出 a, b, c
// 将 NodeList 转换为数组
const nodeList = document.querySelectorAll('div');
const divArray = [...nodeList]; // 现在可以使用 map, filter 等数组方法
const texts = divArray.map(div => div.textContent);
5. 字符串展开
将字符串的每个字符展开为数组元素。
const str = 'hello';
const chars = [...str]; // ['h', 'e', 'l', 'l', 'o']
console.log(Math.max(...[...'321'])); // 3 (将字符串 '321' 展开为 ['3','2','1'],再作为参数传给 Math.max)
三、 与其他操作符的结合
- 与解构赋值结合
- 与模板字符串结合
四、 注意事项
- 浅拷贝:无论是数组还是对象,扩展运算符进行的都是浅拷贝。如果对象包含嵌套的对象或数组,修改嵌套部分会影响原对象。
- 可枚举属性:对象扩展运算符只复制对象自身的、可枚举的属性。
- 不复制原型:扩展运算符不会复制对象的原型链。
null
和undefined
:在对象扩展中,null
和undefined
会被忽略,不会报错。const obj = { ...null, ...undefined, a: 1 }; // { a: 1 }
- 性能:对于大型数组或对象,扩展运算符可能会有性能开销,因为它需要遍历并创建新结构。
总结
ES6 的扩展运算符 ...
是一个语法糖,它的主要作用是展开可迭代对象或对象的元素/属性。主要使用场景包括:
- 数组:合并、复制、作为函数参数、转换类数组对象。
- 对象:合并、复制、添加/修改属性(ES2018)。
- 字符串:转换为字符数组。
它让代码更加简洁、直观和函数式,是现代 JavaScript 开发中不可或缺的工具。理解其“展开”的本质,并注意其“浅拷贝”的特性,是正确使用它的关键。
THE END