JavaScript展开运算符(...)是处理可迭代对象和类数组的核心工具,用于解构、拼接与透传值;它提供浅拷贝能力,但嵌套结构仍为引用,且仅支持可迭代值,否则抛TypeError。

JavaScript 展开运算符(...)不是语法糖,而是处理“可迭代对象”和“类数组”的核心工具——它解决的是值的解构、拼接与透传问题,不是为了写得更短而存在。
复制数组或对象时为什么不能直接用 =?
因为 = 只是赋引用,修改副本会污染原数据。展开运算符提供浅层拷贝能力:
-
[...arr]创建新数组,不共享内存;{...obj}创建新对象,只拷贝自身可枚举属性 - 注意:嵌套对象/数组仍为引用,
{...obj}不会深拷贝obj.nested - 函数参数中用
function f(...args)收集剩余参数,此时args是真数组,可直接调用.map()等方法
... 在函数调用中替代 .apply()
以前要展开数组作为参数必须写 Math.max.apply(null, arr),现在直接写 Math.max(...arr)。
- 支持任意可迭代对象:
Math.min(...new Set([1,2,3]))合法 - 但一次只能展开一个:
fn(...a, ...b, ...c)可以,fn(...a, 42, ...b)也合法 - 错误用法:
fn(...a, 42, ...b, ...c)没问题;但fn(...a, ...b, 42)也没问题——顺序不关键,关键是不能在非可迭代位置用...
合并数组或对象时,... 和 Object.assign() 有什么区别?
行为几乎一致,但有细微差异:
立即学习“Java免费学习笔记(深入)”;
-
{...a, ...b}中,b的同名属性会覆盖a;这和Object.assign(a, b)行为相同 - 但
Object.assign()会触发 setter,{...a, ...b}不会(它只读取属性值) -
[...a, ...b]比a.concat(b)更直观,且能混入原始值:[...a, 99, ...b] - 性能上:小数据无差别;大数据量时,
for循环或Array.prototype.push.apply()可能略快,但可读性代价高
哪些地方不能用 ...?常见报错怎么识别?
展开运算符只接受可迭代(iterable)值,否则抛 TypeError: X is not iterable。
- 数字、
null、undefined、普通对象(没实现[Symbol.iterator])都不能直接展开 - 错误示例:
[...42]→ 报错;{...null}→ 报错;const obj = {}; [...obj]→ 报错 - 修复方式:先确保是数组、字符串、Set、Map、arguments、NodeList 等原生可迭代类型;否则手动转成数组,如
[...Object.keys(obj)]或Array.from(obj) - 箭头函数里没有
arguments,所以const f = () => console.log(...arguments)会报错——这里arguments是未声明变量
真正容易被忽略的点是:展开运算符的“惰性”——它不会提前执行迭代器,也不会缓存中间结果;每次展开都是实时遍历。这意味着如果源在展开过程中被修改(比如异步改了数组长度),结果可能出人意料。











