JavaScript 中 Object.assign 在拷贝过程中对访问器属性的处理

作者:袖梨 2026-06-30
Object.assign对访问器属性只执行值的浅拷贝,不保留getter/setter;源访问器被转为目标对象的数据属性,其返回值被固化,setter逻辑完全丢失。

Object.assign 在拷贝过程中不会调用源对象上的 getter,也不会在目标对象上保留 setter;它只执行“属性值”的浅拷贝,对访问器属性(accessor properties)的处理是特殊且容易被忽略的。

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

如果源对象某个属性是通过 get / set 定义的访问器属性,Object.assign 会读取该属性当前的 返回值(即调用 getter),然后把这个值作为普通数据属性赋给目标对象——目标对象上对应键将变成一个可读写的 数据属性,不再具备 getter/setter 行为。

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

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

Object.assign 的行为基于 [[Get]][[Set]] 内部操作,而非 Object.getOwnPropertyDescriptor + Object.defineProperty。因此它不保留属性的 configurableenumerablewritable 等特性,更不会保留 get / set 函数本身。

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

实际例子说明行为差异

例如:

立即学习“Java免费学习笔记(深入)”;

const src = {  _value: 42,  get foo() { return this._value * 2; },  set foo(v) { this._value = v / 2; }};<p>const target = {};Object.assign(target, src);</p><p>console.log(target.foo); // 84(getter 被执行一次,结果被固化)target.foo = 100;console.log(target.foo); // 100(已变成普通数据属性,不再触发 setter)console.log(src._value); // 42(未改变,setter 没生效)

此时 target.foo 是一个纯数据属性,和 src 的访问器逻辑完全脱钩。

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

若需深拷贝或保留访问器、属性描述符等元信息,应避免直接使用 Object.assign

  • Object.getOwnPropertyDescriptors(src) 获取完整属性描述符
  • 再用 Object.defineProperties(target, descriptors) 复制过去
  • 注意:这仍只是浅拷贝,嵌套对象/访问器内部逻辑仍需另行处理

相关文章

精彩推荐