正确拷贝属性描述符需分两步:先创建空对象,再用object.defineproperties()批量定义完整描述符;普通递归和json方法会丢失writable、enumerable、configurable及get/set,因赋值操作覆盖默认描述符。

要正确拷贝对象上的属性描述符(如 writable、enumerable、configurable,以及 get/set 访问器),不能依赖普通递归或 JSON.parse(JSON.stringify()) —— 它们完全忽略描述符,只处理属性值。关键在于:**深拷贝必须分两步走:先创建空对象,再用 Object.defineProperties() 批量定义带完整描述符的新属性。**
为什么普通递归做不到
常规手写深拷贝(比如遍历 for...in 或 Reflect.ownKeys())默认使用赋值操作(target[key] = value),这会触发属性的 set,且新属性始终是默认描述符(writable: true, enumerable: true, configurable: true)。原始对象上设为 readonly 的属性、不可枚举的 Symbol 键、或自定义 getter/setter,全都会丢失。
用 Object.getOwnPropertyDescriptors + defineProperties 实现
这是目前唯一能保留全部自有属性描述符(含访问器、Symbol 键、不可枚举属性)的标准方式:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 调用
Object.getOwnPropertyDescriptors(obj)获取对象所有自有属性的完整描述符对象 - 对每个描述符中的
value或get/set函数做深拷贝(函数可直接复制引用,除非你明确要克隆函数体) - 对
value是引用类型(对象、数组等)的,递归调用深拷贝函数 - 用
Object.defineProperties(newObj, descriptors)一次性将所有描述符应用到新对象上
注意 Symbol 键和不可枚举属性也能被保留
Object.getOwnPropertyDescriptors() 天然包含 Symbol 类型的键和不可枚举属性(只要它们是“自有”的)。例如:
const original = {
a: 1,
get b() { return this.a * 2; }
};
Object.defineProperty(original, 'c', {
value: { nested: true },
writable: false,
enumerable: false,
configurable: true
});
const sym = Symbol('hidden');
original[sym] = 'secret';
<p>// 拷贝后,b 的 getter、c 的不可枚举性、sym 键都会原样存在
const clone = deepCloneWithDescriptors(original);
</p>
需要配合递归处理嵌套值
描述符本身不递归,所以 value 字段如果是对象,仍需手动深拷贝。推荐在构建 descriptors 对象时做判断:
- 若
desc.value是对象/数组/Date/RegExp 等,调用你的深拷贝函数处理 - 若
desc.get或desc.set存在,函数可直接赋值(不深拷贝函数体,因 JS 中函数是引用类型且无法安全序列化) - 若
desc.value是基本类型或null,直接保留
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










