前言
从ES5.0开始引入原生的Json对象,Json对象有两个函数可以使用:
JSON.stringify():用于将一个ECMA的数据类型的值序列化为一个Json TEXT。
JSON.parse():用于将一个Json TEXT反序列化为ECMA的数据类型的值。
Json TEXT归根到底是一个字符串。
-------参考Json数据格式_lengye7的博客-CSDN博客
序列化
Javascript中,value主要分为两类:
第一类是primitive value,第二类是reference value,接下来就围绕着这两类值来研究JSON.stringfy()的默认行为。
JSON.stringify()
该函数接受三个参数,
第一个为必选参数:被序列化的值,一般来说是一个对象。
第二个为可选参数:可以是一个数组或一个函数,它们主要用于过滤序列化的结果。
第三个为可选参数:表示缩进,允许在结果中插入空格缩进。使用第三个参数的前提是,必须使用第二个参数,如果不想使用第二个参数,仅仅只想使用第三个参数,那么可以把第二个参数设置为null。
返回值是序列化的结果。
注:
ES6开始,标准文档写明该函数的返回值为UTF-16编码的JSON风格的字符串。
实际上,该函数的返回值就是一个JSON TEXT,它就是一个字符串,那么所使用的编码,自然就跟字符串的编码一致了。
所以,标准所说的这一点大可不必在意。
另外,文档的ES2021标准的6.1.4部分,明确说了,String的处理使用UTF-16编码,但是在处理String的时候,EcmaScript是以一种非标准的UTF-16方式进行处理,所以自然也会有一些令人困惑的地方。
绝大多数的Js Engine也是采用UTF-16编码,例如V8。
序列化primitive value
ES6中有6种primitive value:undefined,null,string,number,boolean,symbol。
测试代码
let undef=undefined;
console.log(JSON.stringify(undef));
let nul=null;
console.log(JSON.stringify(nul));
let str="str";
console.log(JSON.stringify(str));
let num=34566;
console.log(JSON.stringify(num));
num=-34566;
console.log(JSON.stringify(num));
num=3.145678;
console.log(JSON.stringify(num));
num=-3.145678;
console.log(JSON.stringify(num));
num=3.14e10;
console.log(JSON.stringify(num));
num=-3.14e10;
console.log(JSON.stringify(num));
num=3.14e-10;
console.log(JSON.stringify(num));
num=-3.14e-10;
console.log(JSON.stringify(num));
let bool=false;
console.log(JSON.stringify(bool));
bool=true;
console.log(JSON.stringify(bool));
let sym=Symbol();
console.log(JSON.stringify(sym));
以下测试结果在chrome 97中获得:
可以看到undefined与symbol这两种值竟然被处理了,没有抛出异常,这是出乎我意料的,它们的序列化结果为undefined,这说明对于Json中不允许的值,该函数将返回undefined。
其它的值,如Number,Boolean,String都被正确的转换为Json中对应的值。
笔记:
在后面将看到,如果一个对象中的property的值为undefined,该property将被忽略。
如果一个对象中的property的值为Symbol,该property将被忽略。
Json中所允许的值可以查看:Json数据格式_lengye7的博客-CSDN博客
注意点:
如果number的数值超出了Javascript的表示范围,那么number的序列化结果就为null。
序列化reference value
Javascript中,reference value往往都是对象的reference,在ES6.0中,经常被使用的对象有:
Function,Object,Array,Map,WeakMap,Set,WeakSet,RegExp。
笔记:
1、对于Global和Math,这两个对象来说,他们都是全局单例对象,基本不可能被引用,所以不测试。
2、对于TypedArray这一类的对象,它们实际上是用在需要直接操作内存的地方,很少会直接转换为Json,所以不测试。
3、Number,Boolean,String这三个对象比较特殊,它们被使用的时候,往往是Primitive Value,在前面已经研究过了。
以下所有测试结果均在chrome97版本中获得:
# Function
let funcT=function (){
console.log("这是一个函数!");
}
console.log("function:",JSON.stringify(funcT))
测试结果:
可以看到,function的序列化结果为undefined。
# RegExp
let reg=/1*/;
console.log("RegExp:",JSON.stringify(reg));
测试结果:
可以看到,RegExp的序列化结果为空的object。
# Map
let MapRef=new Map();
MapRef.set('a',1);
MapRef.set('b',2);
MapRef.set('c','测试Map');
console.log("Map:",JSON.stringify(MapRef))
测试结果:
可以看到,Map的序列化结果为空的object。
# WeakMap
let WeakMapRef=new WeakMap();
let o1={};
let o2={};
let o3={};
WeakMapRef.set(o1,123);
WeakMapRef.set(o2,"test");
WeakMapRef.set(o3,"测试");
console.log("WeakMap:",JSON.stringify(WeakMapRef));
测试结果:
可以看到,WeakMap的序列化结果为空的object。
# Set
let SetRef=new Set();
SetRef.add(1);
SetRef.add(5);
SetRef.add("test");
console.log("SetRef:",JSON.stringify(SetRef));
测试结果:
可以看到,Set的序列化结果为空的object。
# WeakSet
let WeakSetRef=new WeakSet();
let o1={};
let o2={};
let o3={};
WeakSetRef.add(o1);
WeakSetRef.add(o2);
WeakSetRef.add(o3);
console.log("WeakSet:",JSON.stringify(WeakSetRef));
测试结果:
可以看到,WeakSet的序列化结果为空的object。
# Date
Date通过JSON.stringify()处理会变成String。
let DateRef=new Date(2022,4,1);
console.log("DateRef:",JSON.stringify(DateRef));
测试结果:
这里有一个坑,我们通过new Date()创建的是本地的时间,但是通过序列化之后,该本地时间被转化为了UTC时间。
如果需要转换为本地时间,那么就需要自行处理了。
当一个数组中或者一个Object中包含一个Date对象,那么就可以通过JSON.stringify()的第二个参数,对它进行处理了。
# Array
JSON.stringify会一次对数组成员进行处理,
空的数组
console.log("Arr:",JSON.stringify([]));
测试结果:
可以看到,空的数组的序列化结果为空的Array。
包含Primitive Value的数组
let undef=undefined;
let nul=null;
let str="str";
let num=34566;
let num1=-34566;
let num2=3.145678;
let num3=-3.145678;
let num4=3.14e10;
let num5=-3.14e10;
let num6=3.14e-10;
let num7=-3.14e-10;
let bool1=false;
let bool2=true;
let sym=Symbol();
let arrPrim=[
undef,
nul,
num,
num1,
num2,
num3,
num4,
num5,
num6,
num7,
bool1,
bool2,
sym
]
console.log("ArrPrim",JSON.stringify(arrPrim));
测试结果:
可以看到,对于undefined的处理结果为null,对于symbol的处理结果也为null,这与之前的Primitive Value的处理结果有所区别。其它的没有什么变化。
这次发现了一个新的点。
之前Primitive Value中,对undefined以及symbol的处理结果都是undefined,可以查看序列化Primitive Value部分。
包含Reference Value的数组
let reg=/1*/;
let funcT=function (){
console.log("这是一个函数!");
}
let MapRef=new Map();
MapRef.set('a',1);
MapRef.set('b',2);
MapRef.set('c','测试Map');
let WeakMapRef=new WeakMap();
let o1={
"测试1":113123,
func:funcT,
};
let o2={};
let o3={
obj1:o1,
name1:1,
name2:"str"
};
WeakMapRef.set(o1,123);
WeakMapRef.set(o2,"test");
WeakMapRef.set(o3,"测试");
let SetRef=new Set();
SetRef.add(1);
SetRef.add(5);
SetRef.add("test");
let WeakSetRef=new WeakSet();
WeakSetRef.add(o1);
WeakSetRef.add(o2);
WeakSetRef.add(o3);
let arr=[funcT,o1,o2,o3,reg,MapRef,WeakMapRef,SetRef,WeakSetRef,[]]
console.log("ArrayObj:",JSON.stringify(arr))
测试结果:
可看到,对于function的处理结果为null,这与之前的 # function处理结果有所区别,其它的处理结果没有什么变化。
之前 # function中,对function的处理结果为undefined,可以查看# function部分。
其实,数组的Json序列化逻辑可以简单的总结为以下几个步骤:
把以下步骤记为JA(arr):其中arr是需要序列化的数组
1、创建一个Json类型的Array:Partial。 //显然Partial的初始值为"[]"。
2、令temp为empty。
3、令value依次为arr中的成员:
i、如果value为Primitive Value,则:
1)、如果value不为undefined和symbol,则令temp为对应的Primitive Value的Json序列化结果。
否则,令temp为null。
ii、如果value为Map、WeakMap、Set、WeakSet,则令temp为"{}"。
iii、如果value为Function,则令temp为null。
iv、如果value为Object,则令temp=JO(value)。
v、如果value为Array,则令temp=JA(value)。
vi、如果temp不为empty,则将empty放入Partial之中。
4、返回Partial。
----参考ECMAScript 2021
注:这只是一个简单的总结版本,真实的处理算法比这个复杂多了。
# Object
空的Object
console.log("Obj:",JSON.stringify({}));
测试结果:
包含Primitive Value
let undef=undefined;
let nul=null;
let str="str";
let num=34566;
let num1=-34566;
let num2=3.145678;
let num3=-3.145678;
let num4=3.14e10;
let num5=-3.14e10;
let num6=3.14e-10;
let num7=-3.14e-10;
let bool1=false;
let bool2=true;
let sym=Symbol();
let objPrim={
name1:undef,
name2:nul,
name3:str,
name4:num,
name5:num1,
name6:num2,
name7:num3,
name8:num4,
name9:num5,
name10:num6,
name11:num7,
name12:bool1,
name13:bool2,
name14:sym
}
console.log("ObjePrim:",JSON.stringify(objPrim));
测试结果:
可以看到,undefined直接被忽略了,symbol也直接被忽略了,这与之前Primitvie Value处理的结果有所不同。其它的处理结果没有什么变化。
之前Primitive Value中,对undefined以及symbol的处理结果都是undefined,可以查看序列化Primitive Value部分。
包含Reference Value
let reg=/1*/;
let funcT=function (){
console.log("这是一个函数!");
}
let MapRef=new Map();
MapRef.set('a',1);
MapRef.set('b',2);
MapRef.set('c','测试Map');
let WeakMapRef=new WeakMap();
let o1={
"测试1":113123,
func:funcT,
};
let o2={};
let o3={
obj1:o1,
name1:1,
name2:"str"
};
WeakMapRef.set(o1,123);
WeakMapRef.set(o2,"test");
WeakMapRef.set(o3,"测试");
let SetRef=new Set();
SetRef.add(1);
SetRef.add(5);
SetRef.add("test");
let WeakSetRef=new WeakSet();
WeakSetRef.add(o1);
WeakSetRef.add(o2);
WeakSetRef.add(o3);
let obj={
name1:o1,
name2:funcT,
name3:reg,
name4:MapRef,
name5:WeakMapRef,
name6:SetRef,
name7:WeakSetRef,
name8:[1,2,3,"str1",reg,MapRef]
}
console.log("Object:",JSON.stringify(obj));
测试结果:
可以看到,fucntion直接被忽略了,这与之前# function的处理结果有所不同。其它的处理结果没有什么变化。
之前 # function中,对function的处理结果为undefined,可以查看# function部分。
其实,Object的Json序列化逻辑可以简单的总结为以下几个步骤:
把以下步骤记为JO(obj):其中obj是需要序列化的对象
1、创建一个Json类型的Object:Partial。 //显然Partial的初始值为"{}"。
2、令temp为empty。
3、令value依次为obj的可枚举property:
i、如果value的值为Primitive Value,则:
1)、如果value的值不为undefined和symbol,则令temp为对应的Primitive Value的Json序列化结果。
否则,跳转到vi。(即忽略值类型为undefined和symbol的property。)
ii、如果value的值为Map、WeakMap、Set、WeakSet,则令temp为"{}"。
iii、如果value的值为Function,则跳转到vi。(忽略值类型为function的property)
iv、如果value的值为Object,则令temp=JO(value)。
v、如果value的值为Array,则令temp=JA(value)。
vi、如果temp不为empty,则将empty放入Partial之中。
4、返回Partial。
小结:
1、需要序列化的数组中,最好不要有function,undefined,symbol,因为它们会被处理为null,这会造成语义丢失,同时也混淆了null。
2、Object的序列化中,会直接忽略function,undefined,symbol。
反序列化
反序列化就是将JSON TEXT转换为javascript中的类型。
JSON TEXT一共有7种允许的值:false,true,null,String,Number,Array,Object。
这里只研究Array和Object,因为研究false,true,null,String,Number是没有意义的。对于JSON TEXT来说,它实际上就是一个字符串,如果遇到了JSON TEXT中的false,true,null,String,Number等,直接把它们当做字符串即可。
JSON.parse()
第一个参数为需要反序列化的JSON TEXT。
第二个为可选参数,是一个函数。
返回值是反序列化结果。
# Json Object
前面说到过,Json TEXT其实就是一个字符串,只不过这个字符串有一定的格式要求,那么就可以直接使用Javascript字符串构建一个Json TEXT用作JSON.parse()的参数。
let JsonObject="{\"name1\":12345,\"name2\":\"str\",\"name3\":null,\"name4\":true,\"name5\":false,\"name6\":[null,true,false,\"teststr\",{\"name1\":12345,\"name2\":\"str\",\"name3\":null,\"name4\":true,\"name5\":false,\"name6\":[null,true,false,\"teststr\"]},[1,2,3,4,5]],\"name7\":{\"name1\":12345,\"name2\":\"str\",\"name3\":null,\"name4\":true,\"name5\":false,\"name6\":[null,true,false,\"teststr\"]}}";
console.log("JsonObject:",JsonObject);
let AntiJsonObject=JSON.parse(JsonObject);
console.log("AntiJsonObject:",AntiJsonObject);
哈哈,因为没断行,这有点长啊。
测试结果:
可以看到,值都被正确的反序列化了。
# Json Array
这里只看数组中包含Json Object的情况,毕竟这更接近实际。
let JsonArray="[{\"name1\":12345,\"name2\":\"str\",\"name3\":null,\"name4\":true,\"name5\":false,\"name6\":[null,true,false,\"teststr\"]},{\"name1\":12345,\"name2\":\"str\",\"name3\":null,\"name4\":true,\"name5\":false,\"name6\":[null,true,false,\"teststr\"]},{\"name1\":12345,\"name2\":\"str\",\"name3\":null,\"name4\":true,\"name5\":false,\"name6\":[null,true,false,\"teststr\"]},{\"name1\":12345,\"name2\":\"str\",\"name3\":null,\"name4\":true,\"name5\":false,\"name6\":[null,true,false,\"teststr\"]}]"
console.log("JsonArray:",JsonArray);
let AntiJsonArray=JSON.parse(JsonArray);
console.log("AntiJsonArray:",AntiJsonArray);
测试结果:
可以看到,值都被正确反序列化了。
这里提出一个问题:
在Javascript中,Number是有表示范围的,那么一个JSON TEXT的Number超出了表示范围,它会被正确处理吗?那么前端应该怎么处理才能保持它的精度不丢失呢?
这个问题其实在开发中还是可能会遇到的,因为后端数据中返回的JSON TEXT之中,可能会包含超出JS的Number表示范围的Number数据。
这个问题的答案在深入部分再继续。
深入序列化
先来看一个示例:
let reg=/1*/;
let funcT=function (){
console.log("这是一个函数!");
}
let MapRef=new Map();
MapRef.set('a',1);
MapRef.set('b',2);
MapRef.set('c','测试Map');
let WeakMapRef=new WeakMap();
let o1={
"测试1":113123,
func:funcT,
};
let o2={};
let o3={
obj1:o1,
name1:1,
name2:"str"
};
WeakMapRef.set(o1,123);
WeakMapRef.set(o2,"test");
WeakMapRef.set(o3,"测试");
let SetRef=new Set();
SetRef.add(1);
SetRef.add(5);
SetRef.add("test");
let WeakSetRef=new WeakSet();
WeakSetRef.add(o1);
WeakSetRef.add(o2);
WeakSetRef.add(o3);
let DateRef=new Date(); //获取本地当前时间,现在是北京时间2022/5/2 上午 11:13
let obj={
name1:o1,
name2:funcT,
name3:reg,
name4:MapRef,
name5:WeakMapRef,
name6:SetRef,
name7:WeakSetRef,
name8:[1,2,3,"str1",reg,MapRef],
namedate:DateRef
}
console.log("obj:",JSON.stringify(obj));
测试结果:
可以看到,时间并不是2022年5月2日 上午11:13,而是2022年5月2日 3:14,时间被序列化为了UTC时间,而不是本地时间。
但是,我希望序列化的结果中的时间是本地时间,我应该怎么办呢?
往下看。
再探JSON.stringify()
前面说过JSON.stringify()支持两个可选参数,其中第二个参数可以是一个函数或数组,第三个参数是JSON TEXT需要插入的缩进。
具体作用:
第二个参数:
如果它是一个function,那么该function接受两个参数(key,value),其中key对应于property的name,value对应于property的value。这个函数通常称为过滤函数。
对于Array,可以理解为它的property对应的key为索引值,value就是成员的value。
注意:
对于传统的function,它的this指针指向产生key的那个对象。当第一个参数是一个嵌套的对象的时候,这尤为重要。不过,一般现在都用箭头函数。
如果第一个参数是一个嵌套的对象,那么该过滤函数会施加于每一个嵌套的对象。
如果value具备一个toJSON()方法,那么传入该过滤函数的value其实是value.toJSON()的返回值。
如果一个key对应的返回值为function,sysmbol,或者undefined,那么该key会被过滤掉。
一般来说,如果第一个参数是一个对象,第一次调用这个过滤函数的时候,key为空的字符串,value为第一个参数。
# 第一个参数为Object
let funcT=function (){
console.log("这是一个函数!");
}
let o1={
"测试1":113123,
func:funcT,
};
let testobj={
name1:1,
name2:2,
name3:o1,
}
let JsonObject=JSON.stringify(testobj,(key,value)=>{
console.log(key,value);
return value;
});
测试结果:
可以看到,对象中的property的name与value对应上了,嵌套对象的property的name与value也对应上了,说明该过滤函数会施加在每一个嵌套对象的property上,其中key为property的name,value为property的value。
# 第一个参数为Array
let arr=[1,2,3,4,5]
let JsonObject=JSON.stringify(arr,(key,value)=>{
console.log(key,value);
return value;
});
测试结果:
可以看到,索引值与成员的值一一对应,说明对于数组来说,key就是索引值,value就是索引对应的成员的值。
如果它是一个Array,那么该Array就是一个白名单,不在Array中的Property都会被过滤掉。
注意点:
这种方法用来处理嵌套对象会非常麻烦,最好不要用这种方法。
这里给出一个简单的示例:
let foo={
name1:1,
name2:"str"
};
console.log("foo:",JSON.stringify(foo,["name1"]));
测试结果:
可以看到,结果中只有name1,name2被过滤掉了。
# 典型应用
前面说到,本地时间会被JSON.stringify()直接序列化为UTC时间,那么如果我想序列化为本地时间,那应该怎么做呢?
前置小知识,每一个Date实例中,都有一个toJSON()方法,正是该方法返回了UTC时间,如果想要自定义序列化,就必须处理toJSON()方法。
这里,我直接设置toJSON=null,然后再在过滤函数中操作。
let reg=/1*/;
let funcT=function (){
console.log("这是一个函数!");
};
let MapRef=new Map();
MapRef.set('a',1);
MapRef.set('b',2);
MapRef.set('c','测试Map');
let WeakMapRef=new WeakMap();
let o1={
"测试1":113123,
func:funcT,
};
let o2={};
let o3={
obj1:o1,
name1:1,
name2:"str"
};
WeakMapRef.set(o1,123);
WeakMapRef.set(o2,"test");
WeakMapRef.set(o3,"测试");
let SetRef=new Set();
SetRef.add(1);
SetRef.add(5);
SetRef.add("test");
let WeakSetRef=new WeakSet();
WeakSetRef.add(o1);
WeakSetRef.add(o2);
WeakSetRef.add(o3);
let DateRef=new Date();
DateRef.toJSON=null; //去掉该Date实例的toJSON方法
let obj={
name1:o1,
name2:funcT,
name3:reg,
name4:MapRef,
name5:WeakMapRef,
name6:SetRef,
name7:WeakSetRef,
name8:[1,2,3,"str1",reg,MapRef],
namedate: DateRef
};
let JsonObject=JSON.stringify(obj,(key,value)=>{
switch(key){
case "namedate":
return value.toLocaleString();
default:
return value;
}
});
console.log("JsonObject:",JsonObject);
测试结果:
可以看到,namedate对应的时间为本地时间,实现了我想要的效果。
toJSON()
如果一个对象实现了一个toJSON()方法,那么JSON.stringify()作用于该对象的结果,实际上就是该对象的toJSON()的返回值。
PS
如果在使用过滤函数的时候,发现一个property的value应该是一个对象(假设为A),但是传入的确是一个字符串,那么可能就是对象A定义了toJSON()方法。
let book = {
title: "Professional JavaScript",
authors: [
"Nicholas C. Zakas",
"Matt Frisbie"
],
edition: 4,
year: 2017,
toJSON: function() {
return this.title;
}
};
let jsonText = JSON.stringify(book);
console.log("jsonText:",jsonText);
测试结果:
可以看到,book对象的序列化结果正是它的toJSON()方法的返回值。
注:
非必要,不要给一个对象顶一个toJSON方法,时间久了,可能会忘记这茬事。
深入反序列化
let JsonObject="{\"name1\":123456789123456789123456789123456789123456789123456789,\"name2\":123456,\"name3\":123456789.123456789123456789123456789123456789123456789}";
console.log("JsonObject:",JsonObject);
let AntiJsonObject=JSON.parse(JsonObject);
console.log("AntiJsonObject:",AntiJsonObject);
上面的的JSON TEXT中的Number的长整数很长,小数形式的小数部分很长,它们反序列化的结果如何呢?
测试结果:
可以看到,长整数直接被转换为了指数形式,并且丢失了不少精度。小数形式的小数部分直接被四舍五入截断了。Number并没有被正确的处理。
那么这样的Number可以被正确的处理吗?
那当然是能的啊,哈哈哈。答案就是bigInt这个包能够解决。
GitHub - sidorares/json-bigint: JSON.parse/stringify with bigints support
再探JSON.parse()
前面说过,JSON.parse()的第二个参数是一个可选参数,该参数是一个函数,那么该参数的具体作用是什么呢?
先看parse()的原型:
第二参数是一个函数,其接受两个参数(key,value),key和value是parse通过分析第一个参数的内容产生的,如果第一个参数的反序列化结果中没有key/value对,那么第二个参数就不会被调用了。那么显然,如果第一个参数是不为空的Json Array或者Json Object,那么必然会产生key/value对啊。
另外,如果该函数的返回值是undefined,那么对应的key的将会被删除。如果返回了其他的值,那么该值就是对应的key的值,然后插入到结果中。
# Json Array
如果第一个参数是Json Array,那么第二个参数的key将为Json Array的成员在Json Array中的下标值,value则为对应的成员值。
let JsonArray="[1,\"tesstr\",null,false]";
let AntiJsonArray=JSON.parse(JsonArray,(key,value)=>{
console.log("AntiJsonArrayFunc:",key,value);
});
测试结果:
可以看到,key为索引值,序号与值一 一对应。
然而,数组的这个key/value在实际应用中就是鸡肋。
其实,数组完全可以看作是property名为索引值的对象,那么key为索引值也就不奇怪了。
# Json Object
如果第一个参数是Json Object,那么第二个参数的key将为Json Object的property的name,value则为该property对应的value。
let JsonObject="{\"name1\":123456789123456789123456789123456789123456789123456789,\"name2\":123456,\"name3\":123456789.123456789123456789123456789123456789123456789,\"name4\":\"\\uD840\\uDC00\"}";
let AntiJsonObject=JSON.parse(JsonObject,(key,value)=>{
console.log("AntiJsonObjectFunc:",key,value);
});
测试结果:
可以看到,key就是对应的name,value就是对应的值。
# 一个典型应用
let book = {
title: "Professional JavaScript",
authors: [
"Nicholas C. Zakas",
"Matt Frisbie"
],
edition: 4,
year: 2017,
releaseDate: new Date(2017, 11, 1)
};
let jsonText = JSON.stringify(book);
console.log("jsonText:",jsonText);
let bookCopy = JSON.parse(jsonText,
(key, value) => key == "releaseDate" ? new Date(value) : value);
alert(bookCopy.releaseDate.getFullYear());
测试结果:
以上例子来自《Javascript高级程序设计》。
这只是一个示例,不用在意。
还原函数过滤掉某些key值
let reg=/1*/;
let funcT=function (){
console.log("这是一个函数!");
}
let MapRef=new Map();
MapRef.set('a',1);
MapRef.set('b',2);
MapRef.set('c','测试Map');
let WeakMapRef=new WeakMap();
let o1={
"测试1":113123,
func:funcT,
name7:"这也是一个测试!",
name8:"这是一个测试!"
};
let o2={};
let o3={
obj1:o1,
name1:1,
name2:"str"
};
WeakMapRef.set(o1,123);
WeakMapRef.set(o2,"test");
WeakMapRef.set(o3,"测试");
let SetRef=new Set();
SetRef.add(1);
SetRef.add(5);
SetRef.add("test");
let WeakSetRef=new WeakSet();
WeakSetRef.add(o1);
WeakSetRef.add(o2);
WeakSetRef.add(o3);
let DateRef=new Date(); //获取当前时间,现在是北京时间2022/5/2 上午 11:13
let obj={
name1:o1,
name2:funcT,
name3:reg,
name4:MapRef,
name5:WeakMapRef,
name6:SetRef,
name7:WeakSetRef,
name8:[1,2,3,"str1",reg,MapRef],
namedate:DateRef
}
let JsonObject=JSON.stringify(obj);
console.log("JsonObject:",JsonObject);
let AntiJsonObject=JSON.parse(JsonObject,(key,value)=>{
if(key==="name8"){
return undefined;
}
else{
return value;
}
});
console.log(AntiJsonObject);
上面的代码,删除name8这个key值对应的property。
测试结果:
可以看到,name8以及嵌套的name8对应的property都被删除了。
注意点:
这个还原函数会施加给所有的嵌套对象,如果嵌套对象中有多个相同的key,那么它们都会被还原函数所处理。