展开运算符(...)是JavaScript中实现可迭代对象解构与值平铺的底层能力,用于数组合并、浅拷贝、函数传参等场景,但仅支持实现了Symbol.iterator的对象,不可展开null、undefined等非可迭代值。

展开运算符(...)不是语法糖,它是 JavaScript 中真正改变数据操作方式的底层能力——它把“可迭代”变成“可解构”,把“多个值”变成“单个参数”或“独立元素”。
展开运算符在数组中怎么用?
最常见的是替代 concat() 或手动拼接数组,但它本质是“把数组里的每一项拿出来,平铺到当前位置”。
- 合并数组:
[...arr1, ...arr2]比arr1.concat(arr2)更直观,且支持任意数量数组 - 浅拷贝数组:
[...arr]创建新数组,但嵌套对象仍共享引用(这点常被忽略) - 函数调用传参:
Math.max(...numbers)可直接展开,而Math.max(numbers)会返回NaN - 错误写法:
[...null]或[...undefined]会抛出TypeError: undefined is not iterable
对象展开运算符为什么不能直接深拷贝?
{...obj} 只执行一层浅拷贝:顶层属性被复制,但属性值如果是对象、数组或函数,引用关系不变。
- 正确用途:覆盖默认配置 ——
{...defaults, ...userInput},后项同名属性会覆盖前项 - 陷阱:
const a = { x: { y: 1 } }; const b = {...a}; b.x.y = 2;会导致a.x.y也变成 2 - 不支持类数组对象(如
arguments)直接展开到对象中:{...arguments}会静默失败(仅在严格模式下报错)
展开运算符和剩余参数(...)是同一符号,但语义相反
展开(spread)是“向外打散”,剩余(rest)是“向内收拢”。同一个 ... 在不同位置,行为完全相反。
立即学习“Java免费学习笔记(深入)”;
- 函数定义中:
function f(a, b, ...rest) { }——rest是数组,收集多余参数 - 函数调用中:
f(...[1, 2, 3])—— 展开数组,等价于f(1, 2, 3) - 解构赋值中:
const [first, ...others] = arr;——others是剩余元素组成的数组 - 注意:
...必须是最后一个参数(rest)或最后一个元素(spread),否则语法报错
哪些值不能被展开?展开失败时怎么排查?
只有实现了 Symbol.iterator 的值才能被展开。常见不可展开类型包括 null、undefined、普通对象(无迭代器)、数字、布尔值。
- 报错示例:
console.log(...null);→TypeError: null is not iterable - 安全做法:展开前检查是否为真值且可迭代 ——
Array.isArray(val) || typeof val[Symbol.iterator] === 'function' - 字符串是特例:可展开为 Unicode 字符数组(
[...'??']得到['?', '\u200d', '?'],注意 emoji 组合字符) - NodeList、Map、Set 都可展开;但普通对象不行,除非手动加
Symbol.iterator
最容易被忽略的是:展开运算符不会触发 getter,也不会调用 toString() 或 valueOf();它只认迭代协议。如果你看到展开后结果异常,先查 Symbol.iterator 是否存在,而不是怀疑数据内容。











