JS中原生对象 JSON 知识总结(JSON实现对象的深拷贝)
创始人
2025-06-01 02:25:01
0

第23章 JSON

文章目录

  • 第23章 JSON
    • 23.1、语法
      • 23.1.1 简单值
      • 23.1.2 对象
      • 23.1.3 数组
    • 23.2、解析与序列化
      • 23.2.1 JSON 对象
      • 23.2.2 序列化选项
        • 01. 过滤结果
        • 02. 字符串缩进
        • 03. toJSON() 方法
      • 23.2.3 解析选项
    • 23.3、小结
      • 23.3.1 原生 JSON 对象常用户对象的深拷贝

本章内容

  • 理解 JSON 语法
  • 解析 JSON
  • JSON 序列化

  2006年 Douglas Crockford 在国际互联网工程任务组制定了 JavaScript 对象简谱( JSON, JavaScript Object Notation)标准。实际上,JSON 早在 2001 年就开始使用了。JSON 是 JavaScript 的严格子集,利用 JavaScript 中的几种模式来表示结构化数据。

  理解 JSON 最关键的一点是要把它当成一种数据格式,而不是编程语言。JSON 不属于 JavaScript,它们只是拥有相同的语法而已。很多语言都有解析和序列化 JSON 的内置能力。

23.1、语法

  JSON 语法支持表示 3 种类型的值。

  • 简单值:字符串、数值、布尔值和 null 可以在 JSON 中出现,就像在 JavaScript 中一样。特殊值 undefined 不可以
  • 对象:对象表示有序键/值对。每个值可以是简单值,也可以是复杂类型。
  • 数组:数组表示可以通过数值索引访问的值的有序列表。数组的值可以是任意类型,包括简单值、对象,甚至其他数组。

23.1.1 简单值

  最简单的 JSON 可以是一个数值,数值和字符串等。

4
“Hello World”

  JavaScript 字符串与 JSON 字符串的主要区别是,JSON 字符串必须使用双引号(单引号会导致语法错误)。

  布尔值和 null 本身也是有效的 JSON 值。不过,实践中更多使用 JSON 表示比较复杂的数据结构,其中会包含简单值。

23.1.2 对象

  对象使用与 JavaScript 对象字面量略为不同的方式表示。JavaScript 中的对象字面量:

let person = {name: "Nicholas",age: 29
};
let object = {		// js中属性名加上引号,等同于上面的效果"name": "Nicholas","age" : 29
};

  但 JSON 中的对象必须使用双引号把属性名包围起来。JSON 表示相同的对象的语法是:

{"name": "Nicholas","age": 29
}

  与 JavaScript 对象字面量相比,JSON 主要有三处不同。

  • 没有变量声明( JSON 中没有变量)。
  • 最后没有分号(不需要,因为不是 JavaScript 语句)。
  • 引号将属性名包围起来才是有效的 JSON。

  属性的值可以是简单值或复杂数据类型值,后者可以在对象中再嵌入对象,比如:

{"name": "Nicholas","age": 29,"school": {"name": "Merrimack College","location": "North Andover, MA"}
}

23.1.3 数组

  数组在 JSON 中使用 JavaScript 的数组字面量形式表示。例如,以下是一个 JavaScript 数组:

let values = [25, "hi", true];

  在 JSON 中可以使用类似语法表示相同的数组,同样,这里没有变量,也没有分号

[25, "hi", true]

  数组和对象可以组合使用,以表示更加复杂的数据结构。即数组中的元素可以是对象,或对象中的属性值可以为数组。

23.2、解析与序列化

23.2.1 JSON 对象

  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 字符串无效,则会导致抛出错误

23.2.2 序列化选项

  JSON.stringify() 方法除接收序列化的对象参数外,还可以接收两个参数。用于指定其序列化 JavaScript 对象的方式。

  • 第一个参数:序列化的对象(必输

  • 第二个参数:过滤器结果,可以是一个数组或函数(可选

  • 第三个参数:数字或字符串,控制输出字符串缩进和空格(可选

01. 过滤结果

  • 第二个参数是一个数组:那么 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}
  • 第二个参数是一个函数(称之为替代函数(replacer)):提供的函数接收两个参数:属性名(key)属性值(value)。可以根据这个 key 决定要对相应属性执行什么操作。返回的值就是相应 key 应该包含的结果。注意,返回 undefined 会导致属性被忽略。
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 属性值改为 5000case "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属性),会递归处理内部的每一个对象。可通过下面的实例调试看到每一步执行细节。

02. 字符串缩进

  • 第三个参数是一个数值:表示每一级缩进的空格数。
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。

  • 第三个参数是一个字符串:JSON 字符串中就会使用这个字符串而不是空格来缩进。
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 个字符处截断。

03. toJSON() 方法

  如果对象需要在 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() 时会执行如下步骤:

  1. 对象toJSON() 方法调用 toJSON() 方法获取实际的值, 否则使用默认的序列化。
  2. 如果提供了第二个参数,则应用过滤。传入过滤函数的值就是第 1 步返回的值。
  3. 第 2 步返回的每个值都会相应地进行序列化。
  4. 如果提供了第三个参数,则相应地进行缩进。

  如下例子结合了三个参数去序列化一个对象。

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);	// "Professional JavaScript"
// 输出结果
key: # value:[object Object]
key:title # value:Professional JavaScript
key:authors # value:Nicholas C. Zakas,Matt Frisbie
key:1 # value:Matt Frisbie
key:edition # value:4
key:year # value:2017
key:a # value:[object Object]
key:rea # value:99
key:reb # value:nihao
key:rec # value:[object Object]
key:reca # value:3
{
--"title": "Professional JavaScript",
--"authors": [
----"Nicholas C. Zakas",
----"Matt Frisbie"
--],
--"edition": 4,
--"year": 2017,
--"a": {
----"rea": 99,
----"reb": "nihao",
----"rec": {
------"reca": 3
----}
--}
}

23.2.3 解析选项

  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

23.3、小结

  JSON 是一种轻量级数据格式,可以方便地表示复杂数据结构。这个格式使用 JavaScript 语法的一个子集表示对象、数组、字符串、数值、布尔值和 null。虽然 XML 也能胜任同样的角色,但 JSON 更简洁,JavaScript 支持也更好。更重要的是,所有浏览器都已经原生支持全局 JSON 对象

  ECMAScript 5 定义了原生 JSON 对象,用于将 JavaScript 对象序列化为 JSON 字符串,以及将 JSON 数组解析为 JavaScript 对象。JSON.stringify()JSON.parse() 方法分别用于实现这两种操作。 这两个方法都有一些选项可以用来改变默认的行为,以实现过滤或修改流程。

23.3.1 原生 JSON 对象常用户对象的深拷贝

对象的深浅拷贝可以参考: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));
JSON实现对象深拷贝

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
JAVA多线程知识整理 Java多线程基础 线程的创建和启动 继承Thread类来创建并启动 自定义Thread类的子类&#...
【洛谷 P1090】[NOIP... [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G ...
国民技术LPUART介绍 低功耗通用异步接收器(LPUART) 简介 低功耗通用异步收发器...
城乡供水一体化平台-助力乡村振... 城乡供水一体化管理系统建设方案 城乡供水一体化管理系统是运用云计算、大数据等信息化手段࿰...
程序的循环结构和random库...   第三个参数就是步长     引入文件时记得指明字符格式,否则读入不了 ...
中国版ChatGPT在哪些方面... 目录 一、中国巨大的市场需求 二、中国企业加速创新 三、中国的人工智能发展 四、企业愿景的推进 五、...
报名开启 | 共赴一场 Flu... 2023 年 1 月 25 日,Flutter Forward 大会在肯尼亚首都内罗毕...
汇编00-MASM 和 Vis... Qt源码解析 索引 汇编逆向--- MASM 和 Visual Studio入门 前提知识ÿ...
【简陋Web应用3】实现人脸比... 文章目录🍉 前情提要🌷 效果演示🥝 实现过程1. u...
前缀和与对数器与二分法 1. 前缀和 假设有一个数组,我们想大量频繁的去访问L到R这个区间的和,...
windows安装JDK步骤 一、 下载JDK安装包 下载地址:https://www.oracle.com/jav...
分治法实现合并排序(归并排序)... 🎊【数据结构与算法】专题正在持续更新中,各种数据结构的创建原理与运用✨...
在linux上安装配置node... 目录前言1,关于nodejs2,配置环境变量3,总结 前言...
Linux学习之端口、网络协议... 端口:设备与外界通讯交流的出口 网络协议:   网络协议是指计算机通信网...
Linux内核进程管理并发同步... 并发同步并发 是指在某一时间段内能够处理多个任务的能力,而 并行 是指同一时间能够处理...
opencv学习-HOG LO... 目录1. HOG(Histogram of Oriented Gradients,方向梯度直方图)1...
EEG微状态的功能意义 导读大脑的瞬时全局功能状态反映在其电场结构上。聚类分析方法一致地提取了四种头表面脑电场结构ÿ...
【Unity 手写PBR】Bu... 写在前面 前期积累: GAMES101作业7提高-实现微表面模型你需要了解的知识 【技...