Object.assign 不会保留 getter,而是执行一次取值后复制结果为普通属性;需用 Object.defineProperty 或 getOwnPropertyDescriptors 手动复制访问器属性才能保持响应式行为。

Object.assign 不会执行 getter,而是读取当前返回值后拷贝该值。它对源对象中定义的 getter 属性,会在调用时触发一次取值操作,然后把这次得到的结果作为普通属性值复制到目标对象上——也就是说,getter 的响应式逻辑、副作用或动态计算能力在目标对象中全部丢失。
Getter 属性在 Object.assign 中的实际行为
当源对象某个属性是通过 get 定义的访问器属性时,Object.assign 内部会调用 [[Get]] 操作获取其当前值(即执行 getter 函数),再把这个值赋给目标对象的同名属性(此时目标对象上是一个普通数据属性)。
例如:
const source = {
a: 1,
get b() {
console.log('getter b triggered');
return this.a * 2;
}
};
<p>const target = {};
Object.assign(target, source);
// 控制台输出:'getter b triggered'</p><p>console.log(target.b); // 2(只是个普通数值,不再触发 getter)
console.log('b' in target); // true
console.log(Object.getOwnPropertyDescriptor(target, 'b').get); // undefined(不是访问器,是数据属性)</p>如何保留 getter(或实现“结构化”拷贝)
若需在目标对象中也保持 getter 行为,不能依赖 Object.assign,而应手动定义访问器属性:
立即学习“Java免费学习笔记(深入)”;
- 使用
Object.defineProperty或Object.defineProperties显式复制 getter(和 setter) - 遍历源对象的
Object.getOwnPropertyDescriptors(source),筛选出有get或set的描述符,再定义到目标对象 - 注意:不可枚举属性、Symbol 键、原型链上的 getter 都不会被
Object.assign处理,需额外判断
一个保留 getter 的简易实现
下面函数可将源对象的自有属性(含 getter/setter)完整复制到目标对象:
function assignWithAccessors(target, ...sources) {
sources.forEach(source => {
const descriptors = Object.getOwnPropertyDescriptors(source);
Object.keys(descriptors).forEach(key => {
Object.defineProperty(target, key, descriptors[key]);
});
});
return target;
}
<p>// 使用示例:
const src = {
get now() { return Date.now(); }
};
const dst = {};
assignWithAccessors(dst, src);
console.log(dst.now); // 每次读取都触发 getter,返回新时间戳</p>注意事项与常见误区
-
Object.assign只处理自有可枚举属性,不处理原型链、Symbol 键、不可枚举属性(如constructor) - 即使源对象属性是 getter,只要它返回的是引用类型(如对象、数组),拷贝后仍是浅拷贝——目标对象中的该属性值仍指向同一内存地址
- 如果 getter 内部依赖
this,复制后在目标对象上调用时this指向目标对象,可能引发意外行为(尤其当 getter 访问了源对象特有私有字段时)


















