json深拷贝后实例失去原始类方法的根本原因是json.stringify仅序列化可枚举自有属性,不保留构造函数、原型链及行为逻辑,还原后仅为object.prototype的普通对象。

JSON 方法深拷贝后的实例失去原始类的方法,根本原因在于 JSON.stringify 只序列化可枚举的自有属性值,完全不保留构造函数、原型链和任何行为逻辑。它本质上是“数据快照”,不是“对象复刻”。
原型丢失的本质:从对象到纯数据的降级
JavaScript 中,一个由类(如 class Person)创建的实例,其方法并不存储在实例自身上,而是通过 [[Prototype]] 链委托给 Person.prototype。而 JSON.stringify 的规则是:
- 只遍历对象自身的、可枚举的属性(
Object.keys()范围) - 忽略所有不可枚举属性(包括
constructor、__proto__等) - 不读取、不记录、不还原原型链上的任何内容
- 最终输出的只是键值对组成的字符串,没有类型、没有构造器、没有继承关系
所以 JSON.parse() 还原出来的,永远是一个以 Object.prototype 为唯一原型的普通对象——它和原始类再无血缘关系。
一个直观对比示例
假设有如下类:
class User {
constructor(name) {
this.name = name;
}
greet() {
return `Hello, ${this.name}`;
}
}
const u1 = new User('Alice');
此时 u1.greet() 能调用,是因为 u1.__proto__ === User.prototype。
执行深拷贝:
const u2 = JSON.parse(JSON.stringify(u1));
结果 u2 是:
- 一个普通对象:
{ name: "Alice" } -
u2.__proto__ === Object.prototype(不是User.prototype) -
typeof u2.greet是undefined,调用会报错
为什么不能靠“赋值 prototype”修复?
有人尝试手动补原型:u2.__proto__ = User.prototype。这看似可行,但存在风险:
- 违反了现代 JavaScript 对
__proto__的限制(非标准、已废弃) - 若原始类依赖
this.constructor或私有字段(#field),仍无法恢复 - 多个实例共享同一原型,可能引发意外状态污染
- 无法还原静态方法、getter/setter、Symbol 属性等高级特性
真正保留原型的替代方案
如果必须保留类结构和方法,就不能依赖 JSON 序列化。可行路径有:
-
使用
structuredClone():现代浏览器支持,能保留Date、Map、Set等,但依然不保留函数和原型(和 JSON 方法一样) -
类自带克隆方法:在类中定义
clone(),显式构造新实例并复制关键属性 -
构造函数重建:先获取原对象构造器(
obj.constructor),再用new obj.constructor(...)创建,并逐个赋值自有属性 - 第三方库(如 lodash.cloneDeepWith):支持自定义克隆逻辑,可注入构造器处理逻辑
总之,JSON 深拷贝是“去类型化”的过程,适合 DTO、配置、表单数据等纯数据场景;一旦涉及行为、继承或运行时类型语义,就必须换更精确的克隆策略。











