展开运算符(...)实现浅拷贝语法简洁但有局限:仅适用于数组和类数组对象,对象拷贝忽略不可枚举属性、symbol键、getter/setter及原型链,不处理嵌套对象、特殊类型、循环引用,且undefined键被丢弃。

展开运算符(...)在 JavaScript 中实现浅拷贝确实语法简洁,但简洁不等于万能,需结合数据结构和使用场景理性看待。
展开运算符适用于数组和类数组对象
对数组而言,[...arr] 是最直观的浅拷贝写法,比 arr.slice() 或 Array.from(arr) 更易读、更现代:
-
const newArr = [...oldArr];—— 一行完成,语义清晰 - 支持拼接、过滤等链式操作,例如:
[...arr1, ...arr2].filter(x => x > 0) - 对类数组对象(如
arguments、NodeList)也能直接展开转为真数组
对象浅拷贝需配合对象字面量,语法仍简洁但有隐含限制
用 {...obj} 拷贝对象也简洁,但只复制可枚举的自身属性,且忽略原型链、不可枚举属性、Symbol 键(除非显式列出):
-
const newObj = {...oldObj};—— 看似简单,但无法拷贝getters/setters和方法绑定上下文 - 遇到嵌套对象时仍是浅拷贝,
newObj.nested === oldObj.nested为true - 若源对象含
undefined值,展开后该键会被丢弃(因展开时跳过undefined)
无法处理特殊类型与循环引用
展开运算符本质是遍历+赋值,对 Date、RegExp、Map、Set、Error 等内置对象无效,也无法识别或中断循环引用:
-
{...new Date()}得到空对象{};[...new Set([1,2])]虽能展开,但只是提取值,不保留 Set 实例本身 - 遇到循环引用会直接报错(
TypeError: Converting circular structure to JSON),而展开运算符同样会触发该错误 - 函数、undefined、null 在对象展开中行为不一致:函数可被复制,
null展开为空对象,undefined被忽略
对比其他浅拷贝方式,简洁性有优势,但可读性未必更高
在多人协作或复杂逻辑中,“简洁”可能掩盖意图。比如:
-
Object.assign({}, obj)明确表达“合并到空对象”,意图更直白 -
structuredClone()(ES2022)虽不支持所有环境,但语义最准确——它是深拷贝,且明确区分于展开运算符 - 过度链式展开(如
{...a, ...b, ...c})在键冲突时覆盖逻辑隐含,不如分步处理清晰
展开运算符让浅拷贝从“需要查文档的 API”变成“一眼看懂的语法”,这是它的核心价值;但它不是抽象封装,而是语法糖,理解其边界比记住写法更重要。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











