手写深拷贝需用object.getownpropertydescriptor获取属性描述符并用object.defineproperty复制getter/setter,区分数据属性(递归深拷贝)和访问器属性(直接定义描述符),同时保留enumerable与configurable等元信息。

手写深拷贝时,对象上的 getter 和 setter 描述符默认不会被复制——因为 Object.assign、扩展运算符或简单递归遍历属性值,都只读取 get 的返回值(即执行 getter),而忽略其定义本身;setter 更是完全丢失。
需要通过 Object.getOwnPropertyDescriptor + defineProperty 复制访问器描述符
要保留原对象的 getter/setter,不能靠取值赋值,必须:
- 用
Object.getOwnPropertyDescriptor(obj, key)获取该属性的完整描述符(含get、set、enumerable、configurable) - 对每个属性,判断
desc.get !== undefined || desc.set !== undefined,若是访问器属性,就用Object.defineProperty(target, key, desc)直接定义到新对象上 - 注意:不能在新对象上先赋值再改描述符(比如
target[key] = value),否则会覆盖掉访问器,变成数据属性
区分数据属性和访问器属性,分别处理
一个健壮的手写深拷贝函数需对每个属性做类型判断:
-
数据属性(有
value):递归深拷贝其值(注意 null、Date、RegExp、Array 等类型) -
访问器属性(有
get或set):不执行 getter,直接复制描述符本身(函数引用可直接复用,无需深拷贝) - 混合情况(如只有
get没set)也需支持,例如 Vue 响应式中常见只读计算属性
注意 configurable 和 enumerable 的继承性
描述符中的 configurable 和 enumerable 控制属性行为,深拷贝时应一并保留:
-
enumerable: false的属性不会出现在for...in或Object.keys()中,复制后也应保持隐藏 -
configurable: false表示之后无法删除或改描述符,拷贝目标对象也应设为false才语义一致 - 漏掉这两个字段会导致拷贝后属性可枚举/可配置,与原对象行为不一致
示例:基础版支持 getter/setter 的深拷贝
以下代码片段展示核心逻辑(省略循环引用、Symbol、Map/Set 等高级处理):
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj;
<p>const cloned = Array.isArray(obj) ? [] : {};</p><p>for (const key of Reflect.ownKeys(obj)) {
const desc = Object.getOwnPropertyDescriptor(obj, key);
// 如果是访问器属性,直接定义
if ('get' in desc || 'set' in desc) {
Object.defineProperty(cloned, key, desc);
continue;
}
// 数据属性:递归拷贝值
cloned[key] = deepClone(obj[key]);
}</p><p>return cloned;
}</p>这样就能正确保留 get foo() { return this._foo; } 和 set bar(v) { this._bar = v * 2; } 等定义,而不是只拷贝它们当前的执行结果。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











