jsON已经逐步替代XML被全世界的开发者广泛运用。本文深化解说JavaScript中运用jsON.stringify的一些细节问题。首先简略回顾一下JSON和JavaScript:
不是一切的合法的JSON都是有用的JavaScript;JSON只是一个文本格局;JSON中的数字是十进制。1.JSON.stringify
etfoo={a:2,b:function(){}};JSON.stringify(foo);//”{“a”:2}”
还有哪些特点也不能转化?JSON.stringify函数将一个JavaScript目标转化成文本化的JSON。不能被文本化的特点会被疏忽。foo中特点b的值是函数界说,没有被转化而丢掉。
1.循环引用
假如一个目标的特点值经过某种间接的方式指回该目标自身,那么便是一个循环引用。比如:
varbar={a:{c:foo}};varfoo={b:bar};
特点c指向自己,假如层层解析,将会进入一个无限循环。咱们尝试将其打印出来看看:
letfooStringified=JSON.stringify(foo);console.log(fooStringified);//{“b”:{“a”:{}}}
c的特点指向foo目标,foo目标中的b特点又指向bar目标而无法处理,整个被疏忽而回来空目标。
如下界说(原文中的例子)是无法经过编译的:
letfoo={b:foo};
错误信息:
ReferenceError:fooisnotdefinedatrepl:1:14
在函数式言语Haskell中,由于有LazyEvaluation技术,能够运用相似的界说办法。
2.Symbol和undefined
letfoo={b:undefined};JSON.stringify(foo);//{}//Symbolsfoo.b=Symbol();JSON.stringify(foo);//{}
在数组中,不可被stringify的元素用null填充。例外状况
letfoo=[Symbol(),undefined,function(){},’works’]JSON.stringify(foo);//”[null,null,null,’works’]”
为什么有些特点无法被stringify呢?这样能够保持数组自身的“形状”,也便是每一个元素原本的索引。
由于JSON是一个通用的文本格局,和言语无关。设想假如将函数界说也stringify的话,如何判断是哪种言语,并且经过适宜的方式将其呈现出来将会变得特别复杂。特别是和言语相关的一些特性,比如JavaScript中的Symbol。
ECMASCript官方也特意强调了这一点:
ItdoesnotattempttoimposeECMAScript’sinternaldatarepresentationsonotherprogramminglanguages.Instead,itsharesasmallsubsetofECMAScript’stextualrepresentationswithallotherprogramminglanguages.
2.重写目标toJSON函数
一个绕过目标某些特点无法stringify的办法便是完成目标的toJSON办法来自界说被stringify的目标。由于几乎每一个AJAX调用都会运用JSON.stringify,掌握该技巧将会对处理服务器交互有很大帮助。
和toString答应你将目标中的元素以字符串(string)的形式回来相似,toJSON供给了一种能够将目标中不能stringify的特点转化的办法,使得接下来调用的JSON.stringify能够将其转化成JSON格局。
functionPerson(first,last){this.firstName=first;this.last=last;}Person.prototype.process=function(){returnthis.firstName+”+this.lastName;};letade=newPerson(‘Ade’,’P’);JSON.stringify(ade);//”{“firstName”:”Ade”,”last”:”P”}”
Person实例ade的process函数没有被stringify。假想假如服务器只想要ade的全称,而不是别离获取姓和名,咱们能够直接界说toJSON来到达目的:
Person.prototype.toJSON=function(){return{fullName:this.process();};};letade=newPerson(‘Ade’,’P’);JSON.stringify(ade);//”{“fullName”:”AdeP”}”
界说toJSON的优点是复用性和稳定性,你能够将ade配合任何库运用,传输的数据都将是你经过toJSON界说而回来的fullName。
//jQuery$.post(‘endpoint’,ade);//Angular2this.httpService.post(‘endpoint’,ade)
3.可选参数
JSON.stringify完整的界说如下:
JSON.stringify(value,replacer?,space?)
replacer和space都是可选参数,接下来咱们来别离解说。
replacer是一个过滤函数或则一个数组包括要被stringify的特点名。假如没有界说,默许一切特点都被stringify。
1.数组
只要在数组中的特点被stringify:
letfoo={a:1,b:”string”,c:false};JSON.stringify(foo,[‘a’,’b’]);//”{“a”:1,”b”:”string”}”
嵌套特点也同样会被过滤:
letbar={a:1,b:{c:2}};JSON.stringify(bar,[‘a’,’b’]);//”{“a”:1,”b”:{}}”JSON.stringify(bar,[‘a’,’b’,’c’]);//”{“a”:1,”b”:{“c”:2}}”
界说过滤数组有时候并不能满意需求,那么能够自界说过滤函数。
2.函数
过滤函数以目标中的每一个特点和值作为输入,回来值有以下几种状况:
回来undefined表示疏忽该特点;回来字符串,布尔值或则数字将会被stringify;回来目标将会触发递归调用知道遇到根本类型的特点;回来无法stringify的值将会被疏忽;letbaz={a:1,b:{c:2}};//回来大于1的值letreplacer=function(key,value){if(typeof===’number’){returnvalue>1?value:undefined;}returnvalue;};JSON.stringify(baz,replacer);//”{“b”:{“c”:2}}”
经过改写上面的函数加入恰当的输出,能够看到具体的履行过程:
letobj={a:1,b:{c:2}};lettracer=function(key,value){console.log(‘Key:’,key);console.log(‘Value:’,value);returnvalue;};JSON.stringify(obj,tracer);//Key://Value:Object{a:1,b:Object}//Key:a//Value:1//Key:b//Value:Object{c:2}//Key:c//Value:2
space
你是否意识到调用默许的JSON.stringify回来的值只要一行,并且完全没有空格?假如想要愈加美观的打印出来,那么就需要运用space这个参数了。
我告诉你一个十分简略的办法:经过tab(‘t’)来切割即可。
letspace={a:1,b:{c:2}};//运用制表符JSON.stringify(space,undefined,’t’);//”{//”a”:1,//”b”:{//”c”:2//}//}”JSON.stringify(space,undefined,”);//{“a”:1,”b”:{“c”:2}}//自界说分隔符JSON.stringify(space,undefined,’a’);//”{//a”a”:1,//a”b”:{//aa”c”:2//a}//}”
json.stringify语法实例讲解
工作中经常运用JSON.stringify办法存储localStorage,深复制目标,用的最多的便是榜首个参数,甚至不知道它还有第二个和第三个参数,所以详细的整理了一下JSON.stringify用法和特性,使我们能够真正的能灵活运用这个办法。
语法
1
JSON.stringify(value[,replacer[,space]])
参数
replacer参数
replacer参数可所以一个函数或许一个数组。作为函数,它有两个参数,键(key)和值(value),它们都会被序列化。
值得注意的是,在开始时,replacer函数会被传入一个空字符串作为key值,value代表着要被stringify的这个目标。随后每个目标或数组上的特点会被顺次传入。
总的来说replacer参数便是用来手动疏忽一些不想被序列化的特点,有点类似过滤器的作用
varfoo={
id:1,
name:”sf”,
age:18,
};
//作为函数,函数没有回来值或许回来值为undefined时,疏忽这个特点值
JSON.stringify(foo,(key,value)=>{
if(typeofvalue===”string”){
returnundefined;
}
returnvalue;
});
//{“id”:1,”age”:18}
//作为数组,数组的值代表将被序列化成JSON字符串的特点名
JSON.stringify(foo,[‘id’,”name”]);
//{“id”:1,”name”:”sf”}
space参数
space参数用来控制结果字符串里边的距离。如果是一个数字,则在字符串化时每一等级会比上一等级缩进多这个数字值的空格(最多10个空格);如果是一个字符串,则每一等级会比上一等级多缩进该字符串(或该字符串的前10个字符)。实际运用基本都是用来美化输出。
leta=JSON.stringify({a:1,b:2},null,2);
letb=JSON.stringify({a:1,b:2},null,””);
console.log(a==b);//true
JSON.stringify({a:1,b:2},null,”–“);
//{
//–“a”:1,
//–“b”:2
//}
特性描述
1.undefined、Symbol值、函数
呈现在目标特点值中:undefined、Symbol值、函数,在序列化过程中将会被疏忽
呈现在数组中:undefined、Symbol值、函数会被转化为null
独自转化时:会回来undefined
constobj={
a:”a”,
b:undefined,
c:Symbol(),
d:function(){},
};
JSON.stringify(obj)
//{“a”:”a”}
constarry=[undefined,Symbol(“c”),function(){}];
JSON.stringify(arry);
//[null,null,null]
JSON.stringify(undefined);
//undefined
JSON.stringify(Symbol(111));
//undefined
JSON.stringify(function(){});
//undefined
2.非数组目标的特点不能确保以特定的次序呈现在序列化后的字符串中
正如在榜首特性所说,JSON.stringify()序列化时会疏忽一些特别的值,所以不能确保序列化后的字符串还是以特定的次序呈现(数组在外)。
3.布尔值、数字、字符串的包装目标在序列化过程中会主动转化成对应的原始值
JSON.stringify([newBoolean(true),newNumber(1),newString(“a”)]);
//[true,1,”a”]
4.转化值如果有toJSON()办法,该办法界说什么值将被序列化
constobj={
a:”aaa”,
toJSON(){
return”helloworld”;
},
};
JSON.stringify(obj);
//”helloworld”
5.对包含循环引证的目标(目标之间相互引证,构成无限循环)履行此办法,会抛出错误。
constobj={
name:”loopObj”,
};
constloopObj={
obj,
};
//目标之间构成循环引证,构成闭环
obj.loopObj=loopObj;
JSON.stringify(obj);
//TypeError:ConvertingcircularstructuretoJSON
6.一切以symbol为特点键的特点都会被完全疏忽掉,即便replacer参数中强制指定包含了它们。
.对包含循环引证的目标(目标之间相互引证,构成无限循环)履行此办法,会抛出错误。
constobj={
a:”aaa”,
[Symbol(“foo”)]:”foo”,
};
JSON.stringify(obj);
//{“a”:”aaa”}
JSON.stringify(obj,function(k,v){
if(typeofk===”symbol”){
return”asymbol”;
}
});
//undefined
7.日期调用了toJSON()将其转化为了string字符串(同Date.toISOString()),因而会被当做字符串处理。
JSON.stringify({
date:newDate(“2022-02-02”),
})
//{“date”:”2022-02-02T00:00:00.000Z”}
8.NaN和Infinity格局的数值及null都会被当做null。
JSON.stringify([NaN,Infinity,1/0,Number(“a”)]);
//[null,null,null,null]
9.其他类型的目标,包含Map/Set/WeakMap/WeakSet,仅会序列化可枚举的特点。
//不可枚举的特点默许会被疏忽:
JSON.stringify(
Object.create(null,{
x:{value:”x”,enumerable:false},
y:{value:”y”,enumerable:true},
})
);
//”{“y”:”y”}”
运用
localStorage
localStorage中的键值对总是以字符串的方式存储,所以当我们需要把一个目标存在localStorage中时,只能用JSON.stringify将其转化成字符串存储,运用的时分用JSON.parse办法去取
constuserInfo={user:”admin”};
localStorage.setItem(“userInfo”,JSON.stringify(userInfo));
JSON.parse(localStorage.getItem(“userInfo”));
//{user:’admin’}
目标深复制
运用JSON.parse(JSON.stringify)是完成目标的深复制最简略粗犷的办法。可是由于JSON.stringify的一些特性,会发生问题,例如:
undefined、Symbol、函数,目标中会被疏忽,数组中会被序列化成null。
NaN、Infinity和-Infinity会被序列化成null。
循环引证问题,stringify会报错。
当确定不存在以上情况时,才考虑运用JSON.parse(JSON.stringify)进行深复制。
特点过滤
当接口回来一大堆数据,我们只想存某几个特点的时分,经过replacer函数过滤特点是一个不错的小技巧。
varfoo={
id:1,
name:”sf”,
age:18,
};
localStorage.setItem(“user”,JSON.stringify(foo,[“id”,”name”]));
localStorage.getItem(“user”);
//{“id”:1,”name”:”sf”}