JavaScript Object.assign对访问器属性的拷贝处理

作者:技术拾光

时间:2026-07-07

浏览:386

在JavaScript开发中,你可能觉得 Object.assign 就是个简单的“复制”方法,把源对象的属性一股脑儿搬到目标对象上就完事了。但其实,它有个很容易踩坑的行为——对访问器属性(就是那些通过 get/set 定义的属性)的处理,远没有你想的那么“智能”。说白了,它只复制属性值,不复制“行

文章目录

在JavaScript开发中,你可能觉得 Object.assign 就是个简单的“复制”方法,把源对象的属性一股脑儿搬到目标对象上就完事了。但其实,它有个很容易踩坑的行为——对访问器属性(就是那些通过 get/set 定义的属性)的处理,远没有你想的那么“智能”。说白了,它只复制属性值,不复制“行为”。

JavaScript Object.assign对访问器属性的拷贝处理

先明确一个核心结论:Object.assign 在拷贝过程中,既不会调用源对象上的 getter,也不会在目标对象上保留 setter。它执行的是纯粹的“属性值”浅拷贝,对访问器属性的处理方式很特殊,也容易被忽略。

访问器属性会被转为数据属性

如果源对象的某个属性是用 get / set 定义的访问器属性,Object.assign 会读取该属性当前的返回值——也就是调用一次 getter,把拿到的值当作普通数据属性直接赋给目标对象。结果就是:目标对象上对应的键变成了一个可读写的普通数据属性,再也没有 getter/setter 行为了。

  • 源对象的 getter 不会被复制,只有它的返回值被“快照”式地写进目标对象
  • 源对象的 setter 完全丢失,目标对象上同名属性再也无法触发任何 setter 逻辑
  • 后续你对目标对象该属性的修改,完全不影响源对象,也不触发任何访问器逻辑

无法继承或还原访问器定义

这种行为背后的机制很简单:Object.assign 基于 [[Get]] 和 [[Set]] 内部操作,而不是 Object.getOwnPropertyDescriptor 加 Object.defineProperty。所以它不会保留属性的 configurable、enumerable、writable 等特性,更别提 get / set 函数本身了。

  • 即便源属性是不可枚举的,只要它能被 for...in 或 Object.keys 遍历到(即 enumerable: true),就会被拷贝
  • 如果你想完整保留访问器定义,必须手动使用 Object.getOwnPropertyDescriptors 配合 Object.defineProperties

实际例子说明行为差异

来看一个具体的例子:

const src = {
  _value: 42,
  get foo() { return this._value * 2; },
  set foo(v) { this._value = v / 2; }
};

const target = {};
Object.assign(target, src);

console.log(target.foo); // 84(getter 被执行一次,结果被固化)
target.foo = 100;
console.log(target.foo); // 100(已变成普通数据属性,不再触发 setter)
console.log(src._value); // 42(未改变,setter 没生效)

看到没?target.foo 现在是一个纯数据属性,和 src 的访问器逻辑彻底脱钩。你后续怎么改它,都不会影响源对象,因为 setter 已经完全丢失了。

需要保留访问器时的替代方案

如果你要深拷贝,或者需要保留访问器以及属性描述符等元信息,那就得换一种方式,别直接上 Object.assign:

  • 用 Object.getOwnPropertyDescriptors(src) 获取完整的属性描述符
  • 再用 Object.defineProperties(target, descriptors) 把它们复制过去
  • 注意:这仍然是浅拷贝,如果嵌套对象或访问器内部逻辑比较复杂,还需要额外处理

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章