json.parse(json.stringify(obj))深拷贝会丢失原型链,因json只序列化可枚举自有属性,忽略函数、symbol、原型等;拷贝后对象__proto__指向object.prototype,导致继承断裂。

直接用 JSON.parse(JSON.stringify(obj)) 做深拷贝,会丢失原型链上的方法和属性,导致“继承断裂”——拷贝后对象不再能调用父类或构造函数原型上定义的方法。
为什么 JSON 方案会导致原型断裂
JSON 序列化只处理可序列化的数据:基本类型、普通对象、数组、Date(转字符串)、RegExp(转空对象)等。它完全忽略函数、Symbol、undefined、原型信息、constructor、getter/setter。执行后生成的是一个纯数据对象,其 __proto__ 指向 Object.prototype,原始的原型链被彻底重置。
- 原对象可能是
new MyClass(),原型链为obj → MyClass.prototype → Object.prototype → null - JSON 拷贝后变成
{...},原型链退化为obj → Object.prototype → null - 所有定义在
MyClass.prototype上的方法(如obj.sayHello())调用时会报TypeError: obj.sayHello is not a function
保留原型链的深拷贝思路
核心是:不依赖序列化,而是手动创建新实例,并递归复制自有属性,同时确保新对象的 __proto__ 或 constructor 正确指向原对象的原型结构。
- 若对象由构造函数创建(如
new User()),优先用该构造函数新建实例,再深拷贝自有属性 - 避免直接赋值
newObj.__proto__ = obj.__proto__(非标准且部分环境不支持),改用Object.setPrototypeOf(newObj, obj.__proto__)或Object.assign(Object.create(obj.__proto__), ...) - 对
Array、Date、RegExp等内置类型,需单独判断并用对应构造器重建(如new Date(obj))
简易可落地的自定义深拷贝函数
以下函数支持保留构造器与原型链,兼容常见内置类型和普通对象:
function deepCopyWithProto(obj) {
if (obj === null || typeof obj !== 'object') return obj;
// 处理 Date、RegExp、Array 等内置类型
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
if (Array.isArray(obj)) return obj.map(item => deepCopyWithProto(item));
// 创建新实例:使用原对象 constructor,若无则 fallback 到 Object
const Ctor = obj.constructor;
const newObj = Ctor === Object || !Ctor ? {} : new Ctor();
// 设置原型链
Object.setPrototypeOf(newObj, Object.getPrototypeOf(obj));
// 递归拷贝自有属性(不含原型属性)
for (const key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
newObj[key] = deepCopyWithProto(obj[key]);
}
}
return newObj;
}
使用示例:
function Person(name) { this.name = name; }
Person.prototype.greet = function() { return `Hello, ${this.name}`; };
const p1 = new Person("Alice");
const p2 = deepCopyWithProto(p1);
console.log(p2.greet()); // "Hello, Alice" ✅
console.log(p2 instanceof Person); // true ✅
更稳健的生产级建议
真实项目中,不推荐手写复杂深拷贝逻辑。推荐结合场景选择:
- 需要完整继承语义(含方法、私有字段、Symbol)→ 使用
structuredClone()(现代浏览器 & Node.js 18.7+ 支持),它保留原型、Map/Set、Date、RegExp、ArrayBuffer 等 - 需兼容老环境且控制精细 → 使用 Lodash 的
_.cloneDeep(),它内部做了类型识别与构造器还原 - 自定义类明确可控 → 在类中实现
toJSON()+ 静态fromJSON()方法,或定义clone()实例方法,显式管理深拷贝行为










