JSON拷贝对象丢失原型链是设计使然而非bug,因其本质是序列化数据而非复制对象;它只处理可枚举自有属性,忽略构造函数、原型链、不可枚举属性和方法,parse后一律还原为Object实例。

JSON 方法拷贝普通对象时丢失原型链,不是 bug,而是设计使然——它根本就不是为“复制对象”而生,而是为“序列化数据”服务。
JSON 的本质是数据快照,不是对象复刻
JSON.stringify 只关心可枚举的自有属性(即 Object.keys() 能遍历到的内容),它不读取、不记录、也不传递以下任何信息:
- 构造函数(
constructor) - 原型链(
[[Prototype]]或__proto__) - 不可枚举属性(比如
Object.defineProperty设置的enumerable: false属性) - 方法定义(它们通常在原型上,不在实例自身)
所以哪怕你写 class User { getName() { return this.name; } },再用 new User() 创建实例,它的 getName 方法也不在实例上,而在 User.prototype 上。JSON.stringify 看不见它,自然不会保存。
parse 还原后只能挂载到 Object.prototype
JSON.parse() 的唯一任务,是把 JSON 字符串还原成一个标准 JavaScript 对象。这个过程严格遵循 ECMAScript 规范:它总是创建一个以 Object.prototype 为原型的新对象,不会尝试恢复原始类的原型链。
换句话说,无论原始对象来自 Array、Date、Map 还是你自定义的 class ApiClient,还原结果都只是 Object 实例——没有继承关系,没有方法委托,也没有类型语义。
这不是“断链”,而是“降级”
更准确地说,这不是原型链被意外切断,而是从「带行为的对象」主动降级为「纯数据容器」。JavaScript 中,对象的方法和类型标识(如 obj instanceof Date)依赖原型链;而 JSON 拷贝后的对象,只剩最基础的数据结构,所有运行时契约全部失效。
例如:
-
new Date()→JSON.stringify→ 字符串"2026-05-19T00:40:00.000Z"→JSON.parse→ 普通字符串,不再是Date实例 -
new Map([['a', 1]])→ 经过 JSON 处理后变成空对象{},因为Map不可枚举其键值对
想保留原型?得绕开 JSON
如果业务确实需要保持类型和方法,就不能依赖 JSON 流程。可行路径包括:
- 手写深拷贝函数,显式识别构造器并重建实例(如对
Date、RegExp单独处理) - 使用
structuredClone()(现代浏览器支持),它能保留Date、Map、Set、ArrayBuffer等,但依然不支持函数和 Symbol - 用 Lodash 的
cloneDeep(),内部做了大量类型判断与构造器还原逻辑 - 更彻底的做法:不拷贝实例,改用工厂函数或状态重建(如
new User(data))











