2006年 Douglas Crockford 在国际互联网工程任务组制定了 JavaScript 对象简谱( JSON, JavaScript Object Notation)标准。实际上,JSON 早在 2001 年就开始使用了。JSON 是 JavaScript 的严格子集,利用 JavaScript 中的几种模式来表示结构化数据。
理解 JSON 最关键的一点是要把它当成一种数据格式,而不是编程语言。JSON 不属于 JavaScript,它们只是拥有相同的语法而已。很多语言都有解析和序列化 JSON 的内置能力。
JSON 语法支持表示 3 种类型的值。
最简单的 JSON 可以是一个数值,数值和字符串等。
4
“Hello World”
JavaScript 字符串与 JSON 字符串的主要区别是,JSON 字符串必须使用双引号(单引号会导致语法错误)。
布尔值和 null 本身也是有效的 JSON 值。不过,实践中更多使用 JSON 表示比较复杂的数据结构,其中会包含简单值。
对象使用与 JavaScript 对象字面量略为不同的方式表示。JavaScript 中的对象字面量:
let person = {
name: "Nicholas",
age: 29
};
let object = { // js中属性名加上引号,等同于上面的效果
"name": "Nicholas",
"age" : 29
};
但 JSON 中的对象必须使用双引号把属性名包围起来。JSON 表示相同的对象的语法是:
{
"name": "Nicholas",
"age": 29
}
与 JavaScript 对象字面量相比,JSON 主要有三处不同。
属性的值可以是简单值或复杂数据类型值,后者可以在对象中再嵌入对象,比如:
{
"name": "Nicholas",
"age": 29,
"school": {
"name": "Merrimack College",
"location": "North Andover, MA"
}
}
数组在 JSON 中使用 JavaScript 的数组字面量形式表示。例如,以下是一个 JavaScript 数组:
let values = [25, "hi", true];
在 JSON 中可以使用类似语法表示相同的数组,同样,这里没有变量,也没有分号。
[25, "hi", true]
数组和对象可以组合使用,以表示更加复杂的数据结构。即数组中的元素可以是对象,或对象中的属性值可以为数组。
ECMAScript 5 增加了 JSON 全局对象,正式引入解析 JSON 的能力。这个对象在所有主流浏览器中都得到了支持。JSON 对象有两个方法:stringify()
和 parse()
。这两个方法分别可以将 JavaScript 序列化为 JSON 字符串,以及将 JSON 解析为原生 JavaScript 值。
stringify()
方法:把一个 JavaScript 对象序列化为一个不包含空格或缩进 JSON 字符串。let book = {
title: "Professional JavaScript",
authors: [
"Nicholas C. Zakas",
"Matt Frisbie"
],
edition: 4,
year: 2017
};
let jsonText = JSON.stringify(book);
console.log(jsonText);
//{"title":"Professional JavaScript","authors":["Nicholas C. Zakas","Matt Frisbie"],"edition":4,"year":2017}
在序列化 JavaScript 对象时,所有函数和原型成员都会有意地在结果中省略。此外,值为 undefined 的任何属性也会被跳过。最终得到的就是所有实例属性均为有效 JSON 数据类型的表示。
parse()
方法:将 JSON 字符串转化为相应的 JavaScript 值。let bookCopy = JSON.parse(jsonText);
book 和 bookCopy 是两个完全不同的对象,没有任何关系。但是它们拥有相同的属性和值。如果给 JSON.parse()
传入的 JSON 字符串无效,则会导致抛出错误。
JSON.stringify()
方法除接收序列化的对象参数外,还可以接收两个参数。用于指定其序列化 JavaScript 对象的方式。
第一个参数:序列化的对象(必输)
第二个参数:过滤器结果,可以是一个数组或函数(可选)
第三个参数:数字或字符串,控制输出字符串缩进和空格(可选)
JSON.stringify()
返回的结果只会包含该数组中列出的对象属性。let book = {
title: "Professional JavaScript",
authors: [
"Nicholas C. Zakas",
"Matt Frisbie"
],
edition: 4,
year: 2017
};
let jsonText = JSON.stringify(book, ["title", "edition"]);
console.log(jsonText); // {"title":"Professional JavaScript","edition":4}
let book = {
title: "Professional JavaScript",
authors: [
"Nicholas C. Zakas",
"Matt Frisbie"
],
edition: 4,
year: 2017
};
let jsonText = JSON.stringify(book, (key, value) => {
switch (key) {
case "authors":
return value.join(",") // 用 ,分隔 authors 属性值
case "year":
return 5000; // year 属性值改为 5000
case "edition":
return undefined; // edition 属性值改为 undefined 所以不显示
default:
return value; // 其他属性直接返回结果
}
});
console.log(jsonText);
// {"title":"Professional JavaScript","authors":"Nicholas C. Zakas,Matt Frisbie","year":5000}
【注意】第一次调用这个函数实际上会传入空字符串 key,值是 book 对象。函数过滤器会应用到要序列化的对象所包含的所有对象(例如下面实例中的a属性),会递归处理内部的每一个对象。可通过下面的实例调试看到每一步执行细节。
let book = {
title: "Professional JavaScript",
authors: [
"Nicholas C. Zakas",
"Matt Frisbie"
],
edition: 4,
year: 2017
};
let jsonText = JSON.stringify(book, null, 4); // 每级缩进4个空格
console.log(jsonText);
// console.log(jsonText) 输出结果
{
"title": "Professional JavaScript",
"authors": [
"Nicholas C. Zakas",
"Matt Frisbie"
],
"edition": 4,
"year": 2017
}
除了缩进,JSON.stringify()
方法还为方便阅读插入了换行符(有缩进就强行换行)。最大缩进值为10,大于10的值会自动设置为10。
let jsonText = JSON.stringify(book, null, "--" );
// console.log(jsonText) 输出结果
{
--"title": "Professional JavaScript",
--"authors": [
----"Nicholas C. Zakas",
----"Matt Frisbie"
--],
--"edition": 4,
--"year": 2017
}
使用字符串时同样有 10 个字符的长度限制。如果字符串长度超过 10,则会在第 10 个字符处截断。
如果对象需要在 JSON.stringify()
之上自定义 JSON 序列化,可以在要序列化的对象中添加 toJSON()
方法。如果对象有 toJSON 方法,JSON.stringify()
就会调用对象的 toJSON()
方法,以 toJSON()
方法返回的值为序列化值 。
let book = {
title: "Professional JavaScript",
authors: [
"Nicholas C. Zakas",
"Matt Frisbie"
],
edition: 4,
year: 2017,
toJSON: function () { // toJSON 方法
return this.title;
}
};
let jsonText = JSON.stringify(book);
console.log(jsonText); // "Professional JavaScript"
toJSON()
函数是 JavaScript 构建类时重要的工具。通过这种方式,您可以控制 JavaScript 如何将你的类实例序列化为 json 字符串。
箭头函数不能用来定义 toJSON()
方法。主要原因是箭头函数的词法作用域是全局作用域,在这种情况下不合适。
toJSON()
方法可以与过滤函数一起使用,把对象传给 JSON.stringify()
时会执行如下步骤:
toJSON()
方法则调用 toJSON()
方法获取实际的值, 否则使用默认的序列化。如下例子结合了三个参数去序列化一个对象。
let book = {
title: "Professional JavaScript",
authors: [
"Nicholas C. Zakas",
"Matt Frisbie"
],
edition: 4,
year: 2017,
a: {
b: 11,
toJSON: function () { // toJSON 方法
return {
rea: 99,
reb: "nihao",
rec: {
reca: 3,
}
}
}
}
};
let jsonText = JSON.stringify(book, (key, value) => {
switch (key) {
default:
console.log("key:" + key + " # value:" + value);
return value;
}
}, "--");
console.log(jsonText);
输出结果:
JSON.parse()
方法除了接受必输的 JSON 字符串外,还可选接受第二个函数参数,这个函数会针对每个键/ 值对都调用一次。称为还原函数(reviver)。该函数也接收两个参数,属性名(key)和属性值 (value),也需要返回值。
如果还原函数返回 undefined,则结果中就会删除相应的键。如果返回了其他任何值,则该值就会成为相应键的值插入到结果中。还原函数经常被用于把日期字符串转换为 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); // {"title":"Professional JavaScript","authors":["Nicholas C. Zakas","Matt Frisbie"],"edition":4,"year":2017,"releaseDate":"2017-11-30T16:00:00.000Z"}
let bookCopy = JSON.parse(jsonText,
(key, value) => key == "releaseDate" ? new Date(value) : value);
console.log(bookCopy.releaseDate.getFullYear()); // 2017
JSON 是一种轻量级数据格式,可以方便地表示复杂数据结构。这个格式使用 JavaScript 语法的一个子集表示对象、数组、字符串、数值、布尔值和 null。虽然 XML 也能胜任同样的角色,但 JSON 更简洁,JavaScript 支持也更好。更重要的是,所有浏览器都已经原生支持全局 JSON 对象。
ECMAScript 5 定义了原生 JSON 对象,用于将 JavaScript 对象序列化为 JSON 字符串,以及将 JSON 数组解析为 JavaScript 对象。JSON.stringify()
和 JSON.parse()
方法分别用于实现这两种操作。 这两个方法都有一些选项可以用来改变默认的行为,以实现过滤或修改流程。
对象的深浅拷贝可以参考:JS 中对象的深浅拷贝
let obj1 = {
a: "a",
b: [
"b1",
"b2"
],
c: true,
d: 2017,
e: {
e1:1,
e2:{
e2a:3
}
}
};
let obj2 = JSON.parse(JSON.stringify(obj1))
console.log("obj1 === obj2?",obj1 === obj2);
console.log("obj1:",JSON.stringify(obj1));
console.log("obj2:",JSON.stringify(obj2));
obj1.b[0]="b99";
obj2.e.e2.e2a = 99;
console.log("obj1修改了b[0]:",JSON.stringify(obj1));
console.log("obj2修改了e.e2.e2a:",JSON.stringify(obj2));