object.assign()和展开运算符无法拷贝getter/setter,因其仅读取返回值而非保留描述符;正确方式是用object.getownpropertydescriptors()获取描述符,再用object.defineproperties()重定义。

拷贝对象时,getter 属性不会被 Object.assign() 或展开运算符自动复制,它们只会把 getter 的返回值当作普通属性值写入目标对象,导致后续访问失去响应式逻辑。这是最容易踩的坑之一。
为什么 getter 在拷贝后失效?
因为 Object.assign() 和 {...obj} 只读取属性的当前值(即调用 getter 后的结果),再以数据属性方式写入新对象——不保留原始的 get 函数定义。原始对象中定义的 getter/setter 是属性描述符(descriptor)的一部分,而这些方法不会被枚举,也不会被浅拷贝工具识别。
正确拷贝 getter 的方法
必须显式获取并重定义属性描述符:
- 用
Object.getOwnPropertyDescriptors(obj)获取所有自有属性的完整描述符(含get、set、enumerable、configurable等) - 用
Object.defineProperties(target, descriptors)将描述符批量应用到新对象上
示例:
const source = {
_value: 42,
get count() { return this._value; },
set count(v) { this._value = Math.max(0, v); }
};
// ❌ 错误:getter 被“执行一次”后变成静态值
const badCopy = { ...source };
console.log(badCopy.count); // 42(只读数字,无法再设值)
// ✅ 正确:保留 getter/setter 行为
const goodCopy = Object.defineProperties({},
Object.getOwnPropertyDescriptors(source)
);
console.log(goodCopy.count); // 42
goodCopy.count = 100;
console.log(goodCopy.count); // 100
注意 Symbol 属性和不可枚举属性
Object.getOwnPropertyDescriptors() 会包含 Symbol 键和不可枚举属性(如上面的 _value),但 Object.assign() 和展开运算符会忽略它们。如果你依赖私有状态(比如用 Symbol 存储内部值),必须确保这些属性也被一并拷贝,否则 getter 可能因找不到底层数据而报错或返回意外值。
深拷贝场景下的处理
如果对象嵌套了带 getter 的子对象,标准深拷贝方案(如 JSON.parse(JSON.stringify()) 或多数第三方库)通常也不支持 getter。此时需自定义克隆逻辑,对每个子对象单独调用 Object.defineProperties,或改用 Proxy + descriptor 遍历等更可控的方式。











