JSON.stringify 深拷贝的弊端

时间对象

如果 obj 里面有时间对象,则 JSON.stringify 后再 JSON.parse 的结果,时间将转为字符串的形式,而不是对象的形式

1
2
3
4
5
6
let obj = {
a:123,
b:[new Date(),new Date()]
}
let newObj = JSON.parse(JSON.stringify(obj));
console.log(obj,newObj);

stringify

RegExp 、Error

如果 obj 里有 RegExp (正则表达式的缩写)、Error 对象,则序列化的结果将只得到空对象;

1
2
3
4
5
6
7
let obj = {
a:123,
b:/aabb[a-z]\d/,
c:new Error('typeError')
}
let newObj = JSON.parse(JSON.stringify(obj));
console.log(obj,newObj);

stringfy

function、undefined、Symbol

如果 obj 里有函数(function),undefined,Symbol 则序列化的结果会把 function 、 undefined 、Symbol 丢失;

1
2
3
4
5
6
7
8
let obj = {
a:123,
fn:function(){let a = 123;console.log(a)},
c:undefined,
d:Symbol('foo')
}
let newObj = JSON.parse(JSON.stringify(obj));
console.log(obj,newObj);

stringfy

如果 Array里有函数(function),undefined,Symbol 则序列化的结果会把 function 、 undefined 、Symbol 转为null;(占位用)

1
2
3
4
5
6
let obj = {
a: 123,
b: [function () { let a = 123; console.log(a) }, undefined, Symbol('foo')]
}
let newObj = JSON.parse(JSON.stringify(obj));
console.log(obj, newObj);

stringfy

NaN、Infinity 、-Infinity

如果obj里有 NaN、Infinity 和 -Infinity,则序列化的结果会变成 null

1
2
3
4
5
6
7
8
let obj = {
a:123,
b:NaN,
c:Infinity,
d:-Infinity
}
let newObj = JSON.parse(JSON.stringify(obj));
console.log(obj,newObj);

stringfy

枚举

  • 构造函数

JSON.stringify() 只能序列化对象的可枚举的自有属性,例如如果 obj 中的对象是有构造函数生成的, 则使用 JSON.parse(JSON.stringify(obj)) 深拷贝后,会丢弃对象的 constructor;

1
2
3
4
5
6
7
8
9
10
11
12
class Student{
constructor(){
this.a = 1;
this.b = 2;
}
}
let obj = {
a:123,
b:new Student()
}
let newObj = JSON.parse(JSON.stringify(obj));
console.log(obj,newObj);

stringfy

  • 通过Object.defineProperty手动设置某属性不可枚举:
1
2
3
4
5
6
7
8
9
10
let obj = {
a:123,
b:234
}
Object.defineProperty(obj,'c',{
value:22,
enumerable:false,
})
let newObj = JSON.parse(JSON.stringify(obj));
console.log(obj,newObj);

stringfy

弊端总结

用法简单,然而使用这种方法会有一些隐藏的坑:因为在序列化 JavaScript 对象时,所有函数和原型成员会被有意忽略;

通俗点说,JSON.parse(JSON.stringfy(X)),其中 X 只能是 Number, String, Boolean, Array,扁平对象,即那些能够被 JSON 直接表示的数据结构。

  • Date 对象变为字符串
  • RegExp、Error 对象变为空对象 {}
  • 函数、undefined、Symbol 属性
    • 作为对象value:丢失
    • 作为Array的value:为null
  • NaN、Infinity、-Infinity 变为 null
  • enumerable 为 false 的属性丢失

JSON.stringify() 常用方式

判断是否相等

判断数组是否包含某对象,或者判断对象是否相等

  • 是否相等
1
2
3
4
5
6
7
8
9
let obj = {
a:123,
b:456,
}
let obj2 = {
a:123,
b:456,
}
console.log(JSON.stringify(obj) === JSON.stringify(obj2));//true
  • 是否包含
1
2
3
4
5
6
7
let arr = [
{a:1,b:2},
{a:2,b:3},
{a:3,b:4}
]
let obj = {a:2,b:3}
console.log(JSON.stringify(arr).includes(JSON.stringify(obj)));//true

缓存

  • 让 localStorage / sessionStorage 可以存储对象。

localStorage/sessionStorage 默认只能存储字符串,而实际开发中,我们往往需要存储的数据多为对象类型,那么这里我们就可以在存储时利用 Json.stringify() 将对象转为字符串,取出时利用 JSON.parse() 将字符串转为对象即可。

1
2
sessionStorage.setItem('user',JSON.stringify({name:'aaa',age:16}));
console.log(JSON.parse(sessionStorage.getItem('user')));//{name: "aaa", age: 16}

JSON.stringify()与toString()这两者虽然都可以将目标值转为字符串,但本质上还是有区别的

1
2
3
let arr = [12,12,12];
console.log(JSON.stringify(arr),arr.toString());
//[12,12,12] 12,12,12

参考文献

利用 JSON.stringify 深拷贝的弊端