浅拷贝就够了,因为多数场景只需复制第一层引用以避免原数组被意外修改;展开运算符[...arr]语法简洁直观,但对嵌套对象仍共享引用,深拷贝需用structuredClone()或序列化。

用 ... 克隆数组时,为什么浅拷贝就够了?
因为绝大多数场景下,你只是想复制一层引用结构,避免原数组被意外修改。直接写 [...arr] 就行,比 arr.slice() 或 Array.from(arr) 更直观。
注意:如果数组里有对象或嵌套数组,... 不会递归拷贝——改 clone[0].name 依然会影响原数组里的对象。真需要深克隆,别硬扛,该用 structuredClone() 或序列化方案就用。
- ✅ 正确:
const clone = [...original]; - ❌ 错误:
const clone = ...original;(语法错误,缺少包裹方括号) - ⚠️ 避坑:不要对类数组对象(如
arguments、NodeList)直接展开,先确保它是可迭代的;现代环境可用[...document.querySelectorAll('div')],但 IE 不支持
合并多个数组,... 和 concat() 怎么选?
... 更适合写法简洁、动态数量的合并,比如把几个变量拼成一个新数组;concat() 在旧环境兼容性更好,且不强制解构——它能接受非数组参数(如 arr.concat(1, 2))。
性能上差异微乎其微,但要注意:用展开运算符合并大量数组(比如上万项)可能触发 V8 的“堆栈溢出”限制,此时 concat() 更稳。
- ✅ 动态合并:
const all = [...a, ...b, ...c]; - ✅ 插入单个值:
const withHead = [0, ...arr]; - ⚠️ 避坑:
[...a, b, ...c]中的b如果是数组但没加...,它会被当做一个元素塞进去,不是展开
用 ... 传参给函数,和 apply() 有什么实际区别?
本质一样,都是把数组“摊平”成参数列表,但 ... 是语法层支持,更自然;apply() 是方法调用,需显式指定 this 上下文。
常见于调用内置方法(如 Math.max(...arr))或包装已有函数。注意:不能用于构造函数(new Foo(...args) 在部分老引擎报错),这时得回退到 Reflect.construct()。
- ✅ 替代
apply:Math.max(...numbers)比Math.max.apply(null, numbers)干净 - ✅ 带上下文传参:
fn.call(thisObj, ...args)(call支持展开,apply反而冗余) - ⚠️ 避坑:参数超 65536 个时,V8 会抛
RangeError: Maximum call stack size exceeded,这不是代码问题,是引擎限制
哪些地方不能用 ...?容易被忽略的边界情况
展开运算符只认「可迭代(iterable)」对象。普通对象默认不可迭代,所以 {...obj} 是对象展开,不是数组展开——这是两个语法,别混。
另外,它不能出现在赋值语句左侧(除了解构),也不能在对象字面量里展开非对象值(如 {...null} 会报错)。
- ❌ 数组展开误用于对象:
const arr = [...{a:1}]→ 报错,因为 plain object 不是 iterable - ❌ 忘记 await 就展开:
[...asyncGen()]不行,得先await结果或用for await - ⚠️ 避坑:TypeScript 中,如果数组类型是
any[]或带联合类型的元组,展开后推导可能变宽,必要时加类型断言,如[...(arr as number[])]

















