手写 DeepClone 时关键是在创建新对象时让其[[Prototype]]指向原对象构造函数的prototype;需用Object.create()恢复原型,结合Object.getOwnPropertyNames()、Object.getOwnPropertySymbols()和Object.defineProperty()精确复制属性描述符。

手写 DeepClone 时,要正确恢复对象的原型,关键不是“复制原型链”,而是**在创建新对象时,让它的 [[Prototype]] 指向原对象的构造函数的 prototype**。JavaScript 的原型链本身不可序列化(`JSON.stringify` 会忽略),所以深拷贝必须主动重建这一关系。
判断并保留构造器信息
不能只靠 `typeof` 或 `Array.isArray()`,需用 `Object.prototype.toString.call()` 或 `obj.constructor`(注意:`constructor` 可被篡改,优先用 `Object.getPrototypeOf(obj).constructor`):
- 对普通对象:用 `Object.create(Object.getPrototypeOf(obj))` 创建带正确原型的新空对象
- 对数组:`Object.create(Array.prototype)`,再逐项填充
- 对正则、日期等内置类型:用对应构造函数实例化(如 `new Date(obj)`、`new RegExp(obj)`)
- 对自定义类实例:若 `obj.constructor` 存在且是函数,尝试 `Object.create(obj.constructor.prototype)`,再执行 `constructor.apply(newObj, ...)`(需谨慎处理不可枚举/不可写属性)
避免直接赋值 `__proto__`
`__proto__` 是非标准、已废弃的访问器,应使用标准 API:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
- ✅ 推荐:
Object.create(proto)—— 创建时指定原型 - ✅ 替代:
Object.setPrototypeOf(target, proto)—— 创建后设置(性能稍差,但兼容性好) - ❌ 避免:
obj.__proto__ = proto—— 不推荐用于生产环境
处理不可枚举与 Symbol 属性
仅用 `for...in` 或 `Object.keys()` 会丢失不可枚举属性(如 `constructor`、`toString`)和 `Symbol` 键。需组合使用:
立即学习“Java免费学习笔记(深入)”;
-
Object.getOwnPropertyNames(obj)获取所有可枚举 + 不可枚举字符串键 -
Object.getOwnPropertySymbols(obj)获取所有 Symbol 键 -
Object.getOwnPropertyDescriptor(obj, key)获取属性描述符(含 `writable`、`enumerable`、`configurable`),用于精确还原(尤其 `Object.defineProperty` 场景)
一个简化的健壮实现片段
以下代码展示如何为普通对象和数组恢复原型,并兼顾 Symbol 和属性描述符:
function deepClone(obj, hash = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (hash.has(obj)) return hash.get(obj);
// 获取原型并创建新对象
const proto = Object.getPrototypeOf(obj);
const cloned = Object.create(proto);
hash.set(obj, cloned);
// 合并字符串键 + Symbol 键
const keys = [...Object.getOwnPropertyNames(obj), ...Object.getOwnPropertySymbols(obj)];
for (const key of keys) {
const desc = Object.getOwnPropertyDescriptor(obj, key);
const value = desc.value;
// 递归克隆值(跳过函数、undefined 等)
if (value && typeof value === 'object' && !value.nodeType) {
desc.value = deepClone(value, hash);
}
Object.defineProperty(cloned, key, desc);
}
return cloned;
}

















