
JavaScript 中无法完全可靠地深度克隆任意对象(尤其含私有字段、闭包状态或内部槽的对象),但可通过 Object.create + Object.getOwnPropertyDescriptors 实现保原型的浅克隆,再递归扩展为深度克隆——需注意循环引用、私有成员和闭包绑定等固有限制。
javascript 中无法完全可靠地深度克隆任意对象(尤其含私有字段、闭包状态或内部槽的对象),但可通过 `object.create` + `object.getownpropertydescriptors` 实现**保原型的浅克隆**,再递归扩展为深度克隆——需注意循环引用、私有成员和闭包绑定等固有限制。
在 JavaScript 中,标准的 structuredClone() 仅支持可序列化的值(如普通对象、数组、Date、Map、Set 等),不保留原型链、不复制方法、忽略私有字段与闭包状态。因此,当对一个类实例(如 new Some())调用 structuredClone() 时,返回的是一个无原型、无方法的纯数据对象,导致 b.hi() 报错。
要真正“深克隆并继承原型”,核心思路是:
✅ 重建原型关系 → 使用 Object.create(Object.getPrototypeOf(obj));
✅ 复制自有属性(含方法、getter/setter) → 使用 Object.getOwnPropertyDescriptors() + Object.defineProperties();
✅ 递归处理嵌套对象 → 对每个属性值做类型判断,对 Object 或 Array 类型递归克隆;
✅ 避免循环引用崩溃 → 引入 Map 缓存已克隆对象,实现引用复用。
以下是兼顾清晰性与实用性的深度保原型克隆函数:
function deepCloneWithPrototype(obj, seen = new Map()) {
// 处理 null、原始类型、函数、日期、正则等不可克隆或需特殊处理的值
if (obj === null || typeof obj !== 'object') return obj;
if (typeof obj === 'function') return obj; // 方法通常不应被“克隆”,而是继承自原型
if (obj instanceof Date) return new Date(obj.getTime());
if (obj instanceof RegExp) return new RegExp(obj);
// 检查循环引用
if (seen.has(obj)) return seen.get(obj);
// 创建新对象,继承原对象原型
const cloned = Object.create(Object.getPrototypeOf(obj));
seen.set(obj, cloned);
// 获取所有自有属性描述符并递归克隆值
const descriptors = Object.getOwnPropertyDescriptors(obj);
for (const [key, desc] of Object.entries(descriptors)) {
if ('value' in desc) {
cloned[key] = deepCloneWithPrototype(desc.value, seen);
} else {
// 复制 getter/setter(注意:闭包状态仍无法迁移)
if (desc.get) cloned.__defineGetter__(key, desc.get);
if (desc.set) cloned.__defineSetter__(key, desc.set);
}
}
return cloned;
}使用示例:
class Some {
constructor(name) {
this.name = name;
this.data = { version: 1 };
}
hi() { return `Hi, ${this.name}!`; }
get upperName() { return this.name.toUpperCase(); }
}
const a = new Some('Alice');
a.data.nested = { flag: true };
const b = deepCloneWithPrototype(a);
console.log(b.hi()); // "Hi, Alice!"
console.log(b.upperName); // "ALICE"
console.log(b.data === a.data); // false(深度克隆生效)
console.log(b.data.nested === a.data.nested); // false⚠️ 重要限制与注意事项:
- 私有字段(#field)无法访问,故无法克隆:任何尝试都将丢失私有状态;
- 闭包捕获的变量无法迁移:如构造函数中 const secret = 'x'; this.get = () => secret,克隆后 get() 仍指向原闭包,修改原对象可能意外影响克隆体;
- 内部插槽(Internal Slots)不可见:Map/Set/Promise/TypedArray 等内置对象的内部状态无法通过 JS API 复制,此类对象建议用其自身 .constructor 构造新实例;
- DOM/Browser API 对象(如 Element、Window)不可克隆:不属于 JS 标准克隆范畴;
- 性能开销:相比 structuredClone,该方案涉及多次反射操作(getOwnPropertyDescriptors 等),大数据结构下需权衡。
? 总结:没有银弹式通用深克隆。若业务场景可控(如仅克隆自定义类实例,不含私有成员与复杂闭包),上述 deepCloneWithPrototype 是合理选择;若需更高可靠性与兼容性,推荐设计可序列化接口(如实现 toJSON() / fromJSON())或采用构造器重建 + 属性赋值模式,从根本上规避克隆难题。

















