json.stringify 不处理原型链,只序列化可枚举自有属性,导致原型方法丢失;恢复继承需重建实例并复制状态,或通过类的 tojson/fromjson 方法分离状态与行为。

JSON.stringify 本身不处理原型链,它只序列化对象的可枚举自有属性。所谓“静默丢失”,其实是设计使然——不是 bug,而是 JSON 标准(RFC 8259)与 JavaScript 原型机制根本不同步的结果。要真正保留继承结构,不能靠序列化还原,得换思路:重建实例 + 复制状态。
为什么原型链一定会丢
当你对 new MyClass() 调用 JSON.stringify,输出的只是一个扁平的纯数据对象(如 {"name": "Alice"})。它没有 constructor、没有 __proto__ 指向 MyClass.prototype,所有定义在原型上的方法(比如 obj.sayHi())在反序列化后都不可用——因为 JSON.parse 返回的是 Object 实例,原型链固定为 Object.prototype。
绕过 JSON,手动重建实例
核心是两步:先用原构造函数创建新实例,再把原始对象的自有属性深拷贝过去。这样新对象的原型链和原对象一致。
- 检查对象是否有
constructor且是函数,优先用new obj.constructor()初始化 - 对 Date、RegExp、Array 等内置类型,单独判断并用对应构造器重建(如
new Date(obj)) - 递归复制自有属性(
Object.getOwnPropertyNames+Object.getOwnPropertyDescriptor),保留 getter/setter 和不可枚举属性 - 避免直接操作
__proto__(非标准),改用Object.setPrototypeOf(newObj, Object.getPrototypeOf(obj))
用 structuredClone 做轻量替代(现代环境)
Chrome 98+、Firefox 94+、Safari 15.4+ 已支持 structuredClone()。它能保留 Map、Set、Date、RegExp、ArrayBuffer 等类型,也支持循环引用,但仍不保留函数和原型链。所以它适合“数据对象”,不适合“类实例”。若你的对象只是带方法的数据容器,可配合 Object.assign(new MyClass(), structuredClone(data)) 补原型。
需要完整继承时,别序列化,改用状态导出
真正健壮的做法是让类自己负责序列化逻辑:
- 在类中定义
toJSON()方法,只返回应持久化的数据字段(避开函数、Symbol) - 提供静态
fromJSON(json)工厂方法:const inst = new this(); Object.assign(inst, json); return inst; - 把原型方法视为“行为契约”,不存也不传;序列化只管“状态”,行为由类定义保障











