object.assign 本质是浅拷贝,仅复制源对象第一层可枚举自有属性,不递归、不处理原型和不可枚举项;常用写法为 object.assign({}, original),但嵌套引用、原型丢失、同名覆盖等是其主要风险。

Object.assign 本质就是把源对象第一层的可枚举自有属性“复制粘贴”到目标对象上,它不递归、不碰原型、不处理不可枚举项——这就是它做浅拷贝的全部逻辑。
怎么用它做浅拷贝?
核心就一句话:传一个空对象作为 target,再把要拷贝的对象当 source。
-
最常用写法:
const copy = Object.assign({}, original)—— 创建新对象,避免污染原对象 -
数组也能用:
const newArr = Object.assign([], oldArr),效果类似slice()或concat() - 基本类型值安全:数字、字符串、布尔值这类属性会被真正复制一份,改 copy 不影响 original
-
引用类型只复制指针:如果 original 里有
{a: {x: 1}},copy.a 和 original.a 指向同一个对象,改copy.a.x就等于改original.a.x
它复制哪些属性?哪些被忽略?
不是所有看起来“属于这个对象”的东西都会被拷贝,它有明确边界:
-
只复制“自身 + 可枚举”属性:比如
Object.create(parent, { own: { value: 1, enumerable: true } })中的own会被复制,parent上的protoProp不会 - Symbol 键属性会被复制(只要它是可枚举的)
-
不可枚举属性被跳过:用
Object.defineProperty(obj, 'hidden', { value: 'x', enumerable: false })定义的属性不会出现在拷贝结果里 -
访问器属性(getter/setter)会被执行并存值:比如
get name() { return this._name.toUpperCase() },拷贝后得到的是调用 getter 的结果,不再是 getter 本身
常见陷阱和风险
这些坑往往在调试时才暴露,但根源都在“浅”字上:
- 嵌套对象/数组共享引用:这是最常踩的坑。拷贝后修改深层结构,原对象跟着变
-
原型链彻底丢失:拷贝结果是纯 plain object,
instanceof判断失效,原型方法全部消失 - 同名属性静默覆盖:多个 source 有相同 key,后出现的值直接替换前面的,没有提示
-
null/undefined 源对象被忽略:传
Object.assign({}, obj1, null, obj2),null不报错也不起作用,容易误以为没生效 -
目标对象被直接修改:如果误把原对象当 target(如
Object.assign(original, {...})),相当于在原对象上加字段,不是拷贝
什么时候别硬用 Object.assign?
它不是万能胶,遇到以下情况建议换方案:
- 需要保留原型或 constructor → 用
Object.setPrototypeOf(clone, Object.getPrototypeOf(obj))手动重建 - 要深拷贝嵌套结构 → 用
structuredClone()(现代环境)、JSON.parse(JSON.stringify())(简单数据)、或 lodash 的cloneDeep - 想合并含继承关系的对象状态 → 先遍历原型链收集各层自有属性,再合并
- 需保留 getter/setter 行为 → 不要用 assign,考虑 Proxy 或手动定义访问器











