JS中扩展运算符...实现对象浅拷贝,本质是将源对象自身可枚举属性复制到新对象,不拷贝原型、不可枚举属性及嵌套对象深层结构,仅铺开一层。

JS 中用扩展运算符 ... 实现对象的浅拷贝,本质是将源对象自身的可枚举属性(不包括原型链上的)逐个复制到一个新对象中,生成一个新引用的对象。
基本写法:直接展开对象
最常用的方式是用 {...obj} 创建一个新对象:
-
只拷贝对象自身可枚举属性,不拷贝不可枚举属性(如
Object.defineProperty设置的enumerable: false属性) -
不拷贝原型链上的属性或方法,新对象的原型是
Object.prototype - 嵌套对象仍为引用——这是“浅拷贝”的关键限制
示例:
const original = { a: 1, b: { c: 2 } };
const copy = { ...original };
copy.a = 99; // ✅ 修改基础类型属性,不影响 original
copy.b.c = 99; // ❌ 修改嵌套对象属性,original.b.c 也会变
console.log(original.b.c); // 输出 99
合并多个对象时顺便实现拷贝
扩展运算符天然支持多对象展开,常用于默认配置 + 用户配置的场景,同时完成拷贝与合并:
- 后面对象的同名属性会覆盖前面的
- 所有展开的对象都只做浅拷贝
示例:
const defaults = { theme: 'light', fontSize: 14 };
const userConfig = { fontSize: 16 };
const finalConfig = { ...defaults, ...userConfig };
// → { theme: 'light', fontSize: 16 }
// finalConfig 是新对象,修改它不会影响 defaults 或 userConfig(但嵌套对象除外)
注意边界情况
扩展运算符对某些值有隐式转换行为,需留意:
-
null 和 undefined 会被忽略:
{...null, ...{a: 1}}等价于{a: 1} -
原始类型(如字符串、数字)会被包装成对象再展开:
{...'abc'}→{0: 'a', 1: 'b', 2: 'c'} - Symbol 属性会被正确拷贝(只要它是可枚举的)
- Getter/setter 不会触发,只拷贝当前取值结果(即执行 getter 后的返回值)
它不是万能的:什么情况下别用
当需要以下能力时,{...obj} 不适用,应改用其他方式(如 structuredClone、深拷贝库或手动递归):
- 拷贝嵌套对象或数组的深层结构(即深拷贝)
- 保留原型链(可用
Object.assign(Object.create(Object.getPrototypeOf(obj)), obj)) - 处理函数、日期、正则、Map/Set、循环引用等特殊对象
- 拷贝不可枚举属性或 Symbol 非可枚举属性
不复杂但容易忽略:扩展运算符是简洁可靠的浅拷贝工具,关键要清楚它只“铺开一层”。


















