JS中实现支持原型链继承的深拷贝函数,关键在于:用WeakMap解决循环引用,通过Object.getPrototypeOf获取原型、Object.create重建原型链,再用Object.getOwnPropertyDescriptors和Object.getOwnPropertySymbols完整复制自有属性及其特性(含symbol、getter/setter等),最后递归拷贝对象值。

JS 中实现支持原型链继承的深拷贝函数,关键在于:既要递归复制对象的自有属性和嵌套结构,又要保留目标对象的原型关系。普通深拷贝(如 JSON 序列化或递归遍历)会丢失原型,得到 plain object;而单纯用 Object.create(obj.__proto__) 又无法处理嵌套引用和循环引用。
核心思路:复制时重建原型链 + 递归处理属性
需要在创建新对象时指定其原型,并逐个拷贝可枚举/自有属性(包括 symbol),同时维护引用映射防止循环引用。不依赖 constructor,而是直接读取源对象的 __proto__ 或 getPrototypeOf。
- 用
WeakMap缓存已拷贝的对象对,检测并复用已有副本,解决循环引用 - 用
Object.getPrototypeOf()获取源对象原型,再用Object.create()创建具有相同原型的新对象 - 遍历所有自有属性(
Object.getOwnPropertyDescriptors()),保留 getter/setter、writable、enumerable、configurable 等特性 - 对 symbol 类型 key 同样处理(
Object.getOwnPropertySymbols())
基础实现(支持原型 + 循环引用 + 属性特性)
以下是一个轻量但完整的实现:
function deepCloneWithProto(obj, map = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (map.has(obj)) return map.get(obj);
// 创建新对象,继承原对象的原型
const proto = Object.getPrototypeOf(obj);
const cloned = Object.create(proto);
// 缓存映射
map.set(obj, cloned);
// 获取所有自有属性描述符(含 symbol)
const descriptors = {
...Object.getOwnPropertyDescriptors(obj),
...Object.getOwnPropertyDescriptors(
Object.getOwnPropertySymbols(obj).reduce((acc, sym) => {
acc[sym] = Object.getOwnPropertyDescriptor(obj, sym);
return acc;
}, {})
)
};
// 逐个定义属性(保留 descriptor 特性)
Object.defineProperties(cloned, descriptors);
// 递归拷贝属性值(只对对象类型深拷贝)
for (const key in descriptors) {
if (Object.prototype.hasOwnProperty.call(descriptors, key)) {
const desc = descriptors[key];
if ('value' in desc && typeof desc.value === 'object' && desc.value !== null) {
cloned[key] = deepCloneWithProto(desc.value, map);
}
}
}
return cloned;
}
使用示例与验证
验证是否保留了原型链和方法调用:
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return `${this.name} makes a sound`;
}
}
class Dog extends Animal {
bark() {
return `${this.name} barks`;
}
}
const dog = new Dog('Charlie');
dog.age = 3;
dog.hobbies = ['fetch', { location: 'park' }];
const cloned = deepCloneWithProto(dog);
console.log(cloned instanceof Dog); // true
console.log(cloned.bark()); // "Charlie barks"
console.log(cloned.age); // 3
console.log(Array.isArray(cloned.hobbies)); // true
console.log(cloned.hobbies[1].location); // "park"
注意:该函数不处理不可枚举属性(如 arguments.callee)、内部插槽(如 Map/Set 内部数据)、或绑定函数的 this 绑定,适用于常规对象、类实例和自定义原型结构。
简化场景下的替代方案
如果只需兼容大多数情况且不强求完整 descriptor 还原,可用更简洁版本:
function simpleDeepClone(obj, map = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (map.has(obj)) return map.get(obj);
const cloned = Object.create(Object.getPrototypeOf(obj));
map.set(obj, cloned);
for (const key of [...Object.keys(obj), ...Object.getOwnPropertySymbols(obj)]) {
const val = obj[key];
cloned[key] = (val !== null && typeof val === 'object')
? simpleDeepClone(val, map)
: val;
}
return cloned;
}
它不保留 setter/getter 和属性修饰符,但对多数业务对象已够用,代码更易读和调试。


















