Object.getOwnPropertyDescriptors 配合 Object.defineProperties 可实现保留不可枚举属性、getter/setter 及 writable/enumerable/configurable 状态的真正浅拷贝,而 Object.assign 和展开运算符仅复制属性值,忽略元信息。

Object.getOwnPropertyDescriptors 配合 Object.defineProperties 可以实现真正保留属性特性的浅拷贝,比简单的 Object.assign 或展开运算符更“完美”——它能复制不可枚举属性、getter/setter、writable/enumerable/configurable 等元信息。
为什么普通浅拷贝不够“完美”
常见方式如 {...obj} 或 Object.assign({}, obj) 只复制属性值,忽略:
- 不可枚举属性(如
Object.prototype.toString) - getter/setter 访问器属性(复制后变成普通数据属性)
- 属性的
writable、enumerable、configurable状态
用 getOwnPropertyDescriptors + defineProperties 实现
核心思路:先获取源对象所有自有属性的完整描述符,再用这些描述符在新对象上逐个定义。
function shallowCopy(obj) {
if (obj == null || typeof obj !== 'object') return obj;
const descriptors = Object.getOwnPropertyDescriptors(obj);
return Object.defineProperties({}, descriptors);
}
这个函数会:
- 保留所有自有属性(包括不可枚举的)
- 原样复刻 getter/setter(不是调用它们,而是定义同名访问器)
- 继承
enumerable、configurable、writable设置
注意原型链和 symbol 属性
该方法只处理自有属性,不复制原型链上的属性。如果需要包含 symbol 键,getOwnPropertyDescriptors 默认已支持(ES2017+),无需额外处理。
若需保留原型,可额外设置:
const copy = Object.defineProperties( Object.create(Object.getPrototypeOf(obj)), Object.getOwnPropertyDescriptors(obj) );
适用场景与限制
适合对属性行为有严格要求的场景,例如:
- 封装配置对象,依赖
configurable: false防篡改 - 拷贝含计算属性(getter)的对象,保持响应式逻辑
- 测试中构造具有特定属性特性的模拟对象
但它仍是浅拷贝:嵌套对象/数组不会递归处理,引用关系保持不变。











