浅拷贝默认丢失访问器属性,因为 Object.assign() 和展开运算符仅读取 getter 返回值并以数据属性形式写入新对象,不复制 get/set 函数;需用 Object.getOwnPropertyDescriptor() 和 Object.defineProperty() 手动复制完整描述符。

JavaScript 中浅拷贝无法直接复制访问器属性(getter/setter),因为 Object.assign()、展开运算符 {...obj} 和 Object.keys() 等常规方法只拷贝**可枚举的自有数据属性**,会忽略访问器属性(即使它们是可枚举的)。
为什么浅拷贝默认丢失访问器属性?
访问器属性由 get 和 set 函数定义,存储在属性描述符中,而非属性值本身。而 Object.assign() 和扩展运算符仅读取属性的当前值(即调用 getter 后得到的结果),然后以**数据属性**形式写入新对象——相当于“固化”了那一刻的返回值,不再保留原始的 getter/setter 逻辑。
正确复制访问器属性的方法
必须手动获取并重新定义属性描述符,利用 Object.getOwnPropertyDescriptor() 和 Object.defineProperty():
- 遍历源对象的所有自身属性(包括不可枚举的),用
Object.getOwnPropertyNames()+Object.getOwnPropertySymbols() - 对每个属性调用
Object.getOwnPropertyDescriptor(src, key)获取完整描述符 - 用
Object.defineProperty(dst, key, descriptor)将描述符原样复制到目标对象
示例:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
function shallowCopyWithGetters(obj) {
const copy = {};
// 获取所有自有属性(含不可枚举、Symbol)
const keys = [...Object.getOwnPropertyNames(obj), ...Object.getOwnPropertySymbols(obj)];
for (const key of keys) {
const desc = Object.getOwnPropertyDescriptor(obj, key);
Object.defineProperty(copy, key, desc);
}
return copy;
}
<p>const source = {
_value: 42,
get value() { return this._value * 2; },
set value(v) { this._value = v / 2; }
};</p><p>const copied = shallowCopyWithGetters(source);
console.log(copied.value); // 84(正确触发 getter)
copied.value = 100;
console.log(source._value); // 50(setter 也生效)
注意兼容性和边界情况
该方法能保留访问器、不可枚举性、writable/configurable/enumerable 状态,但有几点需留意:
- 如果源属性的 getter/setter 引用了
this,它在新对象中仍指向新对象(正常行为) - 若描述符中存在
value或writable,则它是数据属性,无需特殊处理;若含get或set,则自动按访问器处理 - 不能跨原型链复制——只复制对象自身的属性,不包括继承来的访问器
- 冻结对象(
Object.freeze())或禁止扩展的对象需先解除限制,否则defineProperty会失败
可复用的工具函数
封装成通用函数,支持浅拷贝并保留所有属性特性:
function cloneOwnProperties(obj) {
if (obj === null || typeof obj !== 'object') return obj;
const copy = Array.isArray(obj) ? [] : {};
const props = [
...Object.getOwnPropertyNames(obj),
...Object.getOwnPropertySymbols(obj)
];
for (const prop of props) {
Object.defineProperty(copy, prop, Object.getOwnPropertyDescriptor(obj, prop));
}
return copy;
}
这个函数适用于需要精确保留属性行为的场景,比如配置对象、带计算属性的类实例(非原型继承部分)等。

















