
JSON.stringify() 默认忽略所有非枚举属性(如通过 Object.defineProperty 设置 enumerable: false 的属性),这是其规范行为;若需保留,必须显式启用枚举性或使用自定义序列化逻辑。
`json.stringify()` 默认忽略所有非枚举属性(如通过 `object.defineproperty` 设置 `enumerable: false` 的属性),这是其规范行为;若需保留,必须显式启用枚举性或使用自定义序列化逻辑。
JSON.stringify() 的设计遵循 ECMAScript 规范:它仅遍历对象的可枚举自有属性(enumerable own properties)。这意味着以下两类属性会被自动跳过:
- 通过 Object.defineProperty 显式设置 enumerable: false 的属性;
- 原型链上的属性(即使可枚举);
- Symbol 键属性(除非在 replacer 中显式处理);
- undefined、function 和 symbol 类型的值(无论是否可枚举)。
例如,以下代码输出始终为 {"prop1":"value1"},prop2 消失:
const obj = { prop1: 'value1' };
Object.defineProperty(obj, 'prop2', {
value: 'value2',
enumerable: false, // ❌ 导致被 stringify 忽略
writable: true,
configurable: true
});
console.log(JSON.stringify(obj)); // → {"prop1":"value1"}
✅ 正确做法是将 enumerable 设为 true:
Object.defineProperty(obj, 'prop2', {
value: 'value2',
enumerable: true, // ✅ 现在会被包含
writable: true,
configurable: true
});
console.log(JSON.stringify(obj)); // → {"prop1":"value1","prop2":"value2"}
⚠️ 注意事项:
- 若无法修改属性定义(如第三方库对象或冻结对象),可借助 replacer 函数手动注入非枚举属性:
const allKeys = Object.getOwnPropertyNames(obj); // 获取所有自有属性(含不可枚举) const replacer = (key, value) => { if (key === '') return value; // 根对象 return value; }; // 但 replacer 本身仍不接收不可枚举属性 → 需预处理 const serializableCopy = Object.assign({}, obj); Object.getOwnPropertyNames(obj).forEach(key => { if (!serializableCopy.hasOwnProperty(key)) { serializableCopy[key] = obj[key]; } }); console.log(JSON.stringify(serializableCopy)); - 使用 structuredClone()(现代环境)或 JSON.parse(JSON.stringify()) 无法绕过此限制——它们均依赖标准枚举机制。
- 对于敏感或需精确控制序列化的场景,建议统一采用 replacer + Object.getOwnPropertyDescriptors() 构建完整序列化逻辑。
总结:JSON.stringify 的“丢失”并非 bug,而是按规范工作的结果。根本解法是确保目标属性可枚举;若受限于不可变对象结构,则应主动构造兼容序列化的副本,而非试图修改 stringify 行为本身。











