添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

当程序需要保存数据或需要通过网络连接向另一个程序传输数据时,必须将内存中的数据结构转换为字节或字符的序列,才可以保存或传输。而且,之后可以再被解析或恢复为原来内存中的数据结构。这个将数据结构转换为字节或字符流的方式称为序列化(serialization),也称为编排(marshaling)或制备(pickling)。

Javascript中序列化数据的最简单方式是使用一种称为JSON的序列化格式。JSON是“JavaScript Object Notation”(Javascript对象表示法)的简写形式。顾名思义,这种格式使用Javascript对象和数组字面量语法,将对象和数组形式的数据结构转换为字符串。JSON支持原始数值和字符串,也支持true、false和null值,以及在这些原始值基础上构建起来的对象和数组。JSON不支持其它Javascript类型,如Map、Set、RegExp、Date或定型数组。但不管怎么说,实践已经证明JSON是一种非常通用的数据格式,就连很多非Javascript程序都支持它。

JavaScript通过两个函数:JSON.stringify()和JSON.parse()支持JSON序列化和反序列化。如果一个对象或数组,不包含任何无法序列化的值(如RegExp对象或定型数组),都可以把它传给JSON.stringify()进行序列化。顾名思义,JSON.stringify()返回一个字符串值。而给定JSON.stringify()返回的字符串,可以把它传给JSON.parse()再重建原始的数据结构:

let o = {s:"", n:0,a:[true, false, null]};

let s = JSON.stringify(o); //s=={"s":"","n":0,"a":[true,false,null]}

let copy = JSON.parse(s); //copy=={s: '', n: 0, a: [true, false, null]}

如果不考虑将序列化之后的数据保存到文件中,或者通过网络发送出去,可以使用这对函数(以没有那么高效的方式)创建对象的深度副本:

// 创建任何可序列化对象或数组的深度副本

function deepcopy(o){

return JSON.parse(JSON.stringify(o));

# JSON是JavaScript的子集

数据被序列化为JSON格式后,结果是有效的JavaScript表达式源代码,可以求值为原始数据结构的一个副本。如果在JSON字符串前面加上var data = 并将结果传给eval(),就可以把原始数据结构的一个副本赋值给变量data。但是请不要这样做,因为这是一个巨大的安全漏洞。

JSON有时候也被用为人类友好的配置文件格式。如果你发现自己在手动编辑JSON文件,注意JSON格式是JavaScript的严格子集。不允许有注释,属性名也必须包含在双引号中。

通常,我们只会给JSON.stringify()和JSON.parse()传一个参数。这两个函数其实都可以接收可选的第二个参数,让我们能够扩展JSON的格式。JSON。stringify()还可以接收第三个参数。如果你希望JSON格式字符串对人类友好(比如要用作配置文件),那可以在第二个参数传null,第三个参数传一个数值或字符串。JSON.stringify()的第三个参数告诉它应该把数据格式化为多行缩进,如果第三个参数是个数值,则该数值表示每级缩进的空格数。如果第三个参数是空白符字符串(如'\t'),则每级缩进就使用该字符串。

let o = {s:"test", n:0};

JSON.stringify(o, null, 2);

//   "s": "test",

//   "n": 0

JSON.parse()忽略空白符,因此给JSON.stringify()传第三个参数不会影响将其输出的字符串再转换为原型的数据结构。

# JSON自定义

如果JSON.stringify()再序列化时碰到了JSON格式原生不支持的值,它会查找这个值是否有toJSON()方法。如果有这个方法,就会调用它,然后将其返回值字符串化以替代原始值。Date对象实现了toJSON()方法,这个方法返回与toISOString()方法相同的值。这意味着如果序列化对象中包含Date,则该日期会自动转换为一个字符串。而在解析序列化之后的字符串时,重新创建的数据结构就不会与开始时的完全一样了,因为原来的Date值变成了字符串。

如果想重新创建这个Date对象(或以其他方式修改解析后的对象),可以给JSON.parse()的第二个参数传一个“复活”(reiver)函数。如果指定了这个“复活”函数,该函数就会在解析输入字符串中的每个原始值时被调用(但解析包含这些原始值的对象和数组时不会调用)。调用这个函数时会给它传入两个参数。第一个时属性名,可能时对象属性名,也可能时转换为字符串的数组索引。第二个参数是该对象属性或数组元素对应的原始值。而且,这个函数会作为包含上述原始值的对象或数组的方法调用,因此可以在其中通过this关键字引用包含对象。

复活函数的返回值会编程命名属性的新值。如果复活函数返回它的第二个参数,那么属性保持不变。如果它返回undefined,则相应的命名属性会从对象或数组中删除,即JSON.parse()返回给用户的对象中将不包含该属性。

下面这个例子调用JSON.parse()时传入了复活函数,用于过滤某些属性并重新创建Date对象:

除了使用前面提到的toJSON(),JSON.stringify()也支持给它传入一个数组或函数作为第二个参数来自定义其输出字符串。

JavaScript