为何JSON序列化后的对象与原对象存在差异?React组件场景解析
关于React状态处理中JSON序列化前后对象差异的问题解答
首先咱们来看你的代码里newState和newStateIntermediate产生差异的具体原因,主要有这几个可能性:
1. 引用类型的浅拷贝 vs 深拷贝
你的newState是用对象展开语法创建的:{...state, files: deleteAllFromTo(state.files), container}。这里的关键在于deleteAllFromTo(state.files)这个函数的行为:
- 如果这个函数直接修改了传入的
state.files数组/对象,然后返回原引用(而不是创建一个新的数组/对象返回),那newState.files其实还是指向原state.files的内存地址。后续如果有其他代码修改了原state.files,你console.log(newState)的时候看到的就是修改后的值。 - 而
JSON.parse(JSON.stringify(newState))是做了一次完整的深拷贝,它会把newState里所有嵌套的引用类型都复制成新的独立对象/数组,相当于拍了一张序列化瞬间的快照。所以newStateIntermediate.files是完全独立的,不会受原state.files后续修改的影响,这就导致两者打印出来的内容不一样。
另外,React在严格模式下会冻结state对象防止直接修改,newState基于冻结对象展开后可能还是冻结状态,而JSON序列化后的newStateIntermediate是普通的可修改对象,在浏览器控制台里查看时,冻结对象会有特殊标记(比如灰色显示或者标注[冻结]),这也会让两者看起来不一样。
2. JSON序列化的类型局限性
JSON只能处理有限的几种数据类型:字符串、数字、布尔值、null、数组、普通对象。如果你的state或者newState里包含以下类型,序列化后会被改变或丢失:
- 函数/undefined/Symbol:这些类型会被直接忽略,不会出现在
newStateIntermediate里; - Date对象:会被转成ISO格式的字符串(比如
"2024-05-20T12:00:00.000Z"),而不是Date实例; - 正则表达式/Map/Set:正则会被转成空对象
{},Map和Set也会被转成空对象; - 不可枚举属性:JSON只序列化对象的可枚举属性,原对象的不可枚举属性会被丢弃;
- 循环引用:如果对象存在循环引用(比如A引用B,B又引用A),
JSON.stringify会直接报错,根本生成不了newStateIntermediate。
如果你的state里包含上述任何一种情况,newState会保留这些类型,但newStateIntermediate会丢失或改变它们,自然两者就有差异了。
接下来聊聊为什么序列化后的对象通常和原对象不同,本质上是JSON的设计限制和序列化机制导致的:
- 类型信息丢失:JSON是一种轻量级的数据交换格式,它不支持JavaScript里的复杂类型(比如自定义类实例、函数、Symbol等)。序列化时这些类型要么被丢弃,要么被转成基础类型,比如自定义类的实例会被转成普通对象,丢失原型链和类方法;
- 深拷贝特性:原对象如果有嵌套的引用类型(比如对象里的对象、数组里的数组),原对象里的嵌套部分是引用,而序列化后的对象是完全独立的深拷贝。当原对象的嵌套部分被修改时,原对象会变化,但序列化后的对象不会;
- 属性过滤:JSON只会序列化对象的可枚举、自有属性,原型链上的属性、不可枚举属性都会被忽略;
- 特殊值处理:比如
NaN、Infinity会被转成null,undefined会被忽略,这些都会导致序列化后的对象和原对象不一致。
举个简单例子:
const obj = { func: () => console.log('hello'), date: new Date(), undef: undefined, nested: { a: 1 } }; const serialized = JSON.parse(JSON.stringify(obj)); console.log(obj); // 包含func、date实例、undef、nested引用 console.log(serialized); // 只有date转成的字符串、nested的深拷贝,func和undef都没了
内容的提问来源于stack exchange,提问作者misterx527
相关产品推荐
相关产品推荐

