Object.assign 无法复制 getter,只获取其返回值;要保留 getter,需用 Object.getOwnPropertyDescriptors + Object.defineProperty。

Object.assign 只会复制源对象的可枚举自有属性值,遇到 getter 属性时,它读取的是 getter 的返回值(即“快照”),而不是把 getter 本身复制过去。所以目标对象上得到的是普通数据属性,不是响应式或动态计算的 getter。
理解问题本质
getter 是访问器属性,属于属性描述符(descriptor)的一部分。Object.assign 内部使用 for...in + hasOwnProperty 遍历,再通过 简单赋值(target[key] = source[key])写入,这一步触发了 getter 的执行,但丢失了原始 descriptor 中的 get、enumerable、configurable 等元信息。
用 Object.defineProperty 手动复制 getter
要真正保留 getter,需获取源属性的完整描述符,并用 defineProperty 逐个定义到目标对象:
function assignWithGetters(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 = {
a: 1,
get b() { return this.a * 2; }
};
const dst = {};
assignWithGetters(dst, src);</p><p>console.log(dst.b); // 2(是真正的 getter,非静态值)
console.log(Object.getOwnPropertyDescriptor(dst, 'b').get); // function b()</p>注意不可枚举和继承属性
Object.getOwnPropertyDescriptors 只获取**自有且可枚举/不可枚举**的属性,不包括原型链上的属性。如果需要复制原型上的 getter(如 Date.prototype.toISOString),需手动遍历原型链并调用 getOwnPropertyDescriptor。但通常业务中只需处理自有属性。
- 避免直接用 for...in 或 Object.keys —— 它们跳过不可枚举属性(包括很多内置 getter)
- setter 同理:Object.getOwnPropertyDescriptors 也保留 set 函数,defineProperty 一并写入即可
- 若目标对象已有同名属性且 writable: false,defineProperty 会报错,建议加 try/catch 或先检查
兼容性与替代方案
Object.getOwnPropertyDescriptors 是 ES2017 特性,IE 完全不支持。如需兼容旧环境:
- 用 Reflect.getOwnPropertyDescriptor + Reflect.defineProperty(ES2015+,语义更清晰)
- 对已知 getter 名称,手动 defineProperty(适合固定结构)
- 不追求“完全复刻”,只关心行为结果时,可用 Proxy 封装源对象,按需拦截 get 操作


















