手写深层原型拷贝函数需先用object.create和object.getprototypeof重建原型链,再递归深拷贝自有可枚举属性,同时用weakmap处理循环引用;不支持函数、promise等不可序列化对象。

要手写一个具备深层原型拷贝能力的克隆函数,核心在于:既要递归复制对象/数组的**自有属性值**(深拷贝),又要正确还原目标对象的**原型链关系**(即让克隆体继承原对象的原型,而非默认的 Object.prototype)。JavaScript 原生的 JSON.parse(JSON.stringify()) 或结构化克隆(structuredClone)都不保留原型;Object.assign 和展开运算符只做浅拷贝且不处理原型。
1. 先获取并重建原型链
克隆对象必须继承其原始原型,否则 instanceof、isPrototypeOf、方法调用等都会失效。可用 Object.getPrototypeOf(obj) 获取原型,再用 Object.setPrototypeOf(clone, proto) 设置。
- 注意:不能直接用
new obj.constructor(),因为构造函数可能无参、报错、或执行副作用 - 安全做法是创建一个干净空对象,并显式设置其原型
- 对
null原型(如Object.create(null))需特殊处理,设为null
2. 递归深拷贝自有可枚举属性
遍历 Object.keys(obj)(仅自有可枚举属性),对每个值做类型判断与递归处理:
-
null → 返回
null - 基本类型(string / number / boolean / bigint / symbol / undefined)→ 直接返回(不可变)
-
Date →
new Date(obj.getTime()) -
RegExp →
new RegExp(obj)(注意 flags) -
Array → 用
[]创建新数组,递归拷贝每一项 - 普通对象(非 null,非内置类实例) → 递归调用克隆函数自身
-
其他内置对象(如 Map、Set、TypedArray 等) → 按需支持,例如
new Map(obj)(需确保迭代器兼容)
3. 处理循环引用(可选但推荐)
若对象存在自引用或环状结构(如 a.b = a),不加检测会导致无限递归栈溢出。可引入 WeakMap 缓存已克隆的对象映射:
- 每次进入克隆前,先查 WeakMap 是否已有原对象 → 有则直接返回对应克隆体
- 克隆开始前,将
(original → clone)写入 WeakMap,再递归处理属性 - WeakMap 避免内存泄漏,且键必须是对象
4. 完整可运行示例
以下是一个兼顾原型、深拷贝、循环引用的轻量实现(不支持函数、Promise、Error 等不可序列化对象):
function deepCloneWithPrototype(obj, seen = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (seen.has(obj)) return seen.get(obj);
// 创建空对象,继承原对象原型
const proto = Object.getPrototypeOf(obj);
const clone = Object.create(proto);
// 缓存映射,防止循环引用
seen.set(obj, clone);
// 特殊内置类型处理
if (obj instanceof Date) return new Date(obj.getTime());
if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags);
// 数组:用 Array.isArray 更准确(比 toString 判别更可靠)
if (Array.isArray(obj)) {
obj.forEach((item, i) => {
clone[i] = deepCloneWithPrototype(item, seen);
});
return clone;
}
// 普通对象:遍历自有可枚举属性
for (const key of Object.keys(obj)) {
clone[key] = deepCloneWithPrototype(obj[key], seen);
}
return clone;
}
✅ 使用示例:
class Animal { speak() { return 'hi'; } }
const dog = Object.assign(Object.create(Animal.prototype), { name: 'wangcai', age: 3 });
dog.self = dog; // 循环引用
const cloned = deepCloneWithPrototype(dog);
console.log(cloned.name); // 'wangcai'
console.log(cloned.speak()); // 'hi'(原型方法可用)
console.log(cloned instanceof Animal); // true
console.log(cloned.self === cloned); // true(循环引用被正确重建)
不复杂但容易忽略。关键不是“拷得有多深”,而是“原型链是否被忠实地重建”。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











