Javascript的Json序列化与反序列化深入研究笔记

前言

从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()的原型:

parse ( text [ , reviver ] )

第二参数是一个函数,其接受两个参数(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就是对应的值。

# 一个典型应用

  还原函数经常被用于把日期字符串转换为 Date 对象。  
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,那么它们都会被还原函数所处理。 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值