JSON.stringify() 不序列化原型属性,因其仅处理对象自身可枚举、非函数、非 undefined 属性;原型属性属继承所得,被自动忽略。

JavaScript 原型对象本身不能直接序列化导出,因为 JSON.stringify() 会忽略原型链上的属性和方法,只处理对象自身的可枚举、非函数、非 undefined 的属性。
为什么原型属性不会被 JSON 序列化
JSON.stringify() 只遍历对象自身的可枚举属性(即通过 Object.prototype.hasOwnProperty() 为 true 且 propertyIsEnumerable() 返回 true 的属性),而原型上的属性属于继承而来,不会被包含。例如:
function Person(name) { this.name = name; }
Person.prototype.sayHi = function() { return 'Hi!'; };
Person.prototype.age = 25;
const p = new Person('Alice');
console.log(JSON.stringify(p)); // {"name":"Alice"} —— age 和 sayHi 都丢失了
手动提取原型数据并合并导出
若需导出包括原型信息的结构,需主动收集原型链上的自有属性(不含方法),再与实例属性合并:
- 用
Object.getPrototypeOf(obj)获取原型 - 用
Object.getOwnPropertyNames(proto)获取原型上所有自有属性名(含不可枚举) - 过滤掉函数、constructor、
__proto__等,只保留普通数据属性 - 将原型数据浅合并到实例对象(注意避免覆盖同名实例属性)
function serializeWithPrototype(obj) {
const result = { ...obj }; // 先拷贝自身属性
let proto = Object.getPrototypeOf(obj);
while (proto && proto !== Object.prototype) {
Object.getOwnPropertyNames(proto).forEach(key => {
const desc = Object.getOwnPropertyDescriptor(proto, key);
if (desc && desc.value !== undefined && typeof desc.value !== 'function' && key !== 'constructor') {
if (!(key in result)) { // 仅当实例没该属性时才补上
result[key] = desc.value;
}
}
});
proto = Object.getPrototypeOf(proto);
}
return result;
}
console.log(JSON.stringify(serializeWithPrototype(p))); // {"name":"Alice","age":25}
更实用的替代方案:定义可序列化的数据结构
不依赖原型存储数据,而是把需要导出的字段显式放在实例上,或通过方法返回标准化数据:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
立即学习“Java免费学习笔记(深入)”;
- 在构造函数或 class 中初始化所有必要字段(如
this.age = 25) - 提供
toJSON()方法,自定义序列化行为(JSON.stringify()会自动调用它) - 使用
class+static toJSONSchema或exportData()方法统一管理导出逻辑
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
toJSON() {
return { name: this.name, age: this.age, type: 'Person' };
}
}
console.log(JSON.stringify(new Person('Bob', 30))); // {"name":"Bob","age":30,"type":"Person"}
导出带类型/结构信息的完整快照(进阶)
如果确实需要记录原型关系(如用于调试、持久化还原),可额外保存构造函数名和原型链结构,但需配合反序列化逻辑:
- 导出时记录
obj.constructor.name和关键原型属性 - 用
Object.assign()或structuredClone()(现代环境)深拷贝可枚举数据 - 注意:函数、Symbol、Date、RegExp、Map/Set 等仍无法被 JSON 原生支持,需自定义处理器
实际项目中,建议优先用纯数据对象(POJO)承载导出内容,把行为逻辑(原型方法)和状态数据分离,既利于序列化,也更易维护和测试。

















