JSON 的由来
在目前的开发中,JSON是一种非常重要的数据格式,它并不是编程语言,而是一种可以在服务器和客户端之间传输的数据格式。
JSON 的全称是 JavaScript Object Notation(JavaScript对象符号):
- JSON 是由 Douglas Crockford 构想和设计的一种轻量级资料交换格式,算是 JavaScript 的一个子集;
- 但是虽然 JSON 被提出来的时候是主要应用 JavaScript 中,但是目前已经独立于编程语言,可以在各个编程语言中使用;
- 很多编程语言都实现了将 JSON 转成对应模型的方式;
其他的传输格式:
- XML:在早期的网络传输中主要是使用XML来进行数据交换的,但是这种格式在解析、传输等各方面都弱 JSON,所以目前已经很少在被使用了;
- Protobuf:另外一个在网络传输中目前已经越来越多使用的传输格式是protobuf,但是直到2021年的3.x版本才支持JavaScript,所以目前在前端使用的较少;
目前JSON被使用的场景也越来越多:
- 简单值:数字(Number)、字符串(String,不支持单引号)、布尔类型(Boolean)、null 类型; ```json 123
这样是错误的,json 的顶层只能有一个,json 中也不能添加注释 123 “hhh”
2. 对象值:由key、value组成,key是字符串类型,并且必须添加双引号,值可以是简单值、对象值、数组值;```json{"name": "zs","arr": [1, 2, 3],"obj": {"age": 123} 最后一个键值对不能有逗号结尾}
- 数组值:数组的值可以是简单值、对象值、数组值;
[123,"hhh",[1, 2, 3],{"name": "zs"}]
JSON 序列化
我们传输或存储数据处理的都是字符串,比如要存储一个对象到本地存储中,存储的其实是对象的字符串形式。
但是我们存储对象的时候会发现,直接存储对象,对象自动转成的字符串是 [object Object] ,这并不是我们想要的。 ```javascript const obj = { name: ‘zs’, run() { console.log(this.name + ‘ running’) } }
localStorage.setItem(“obj”, obj) console.log( localStorage.getItem(“obj”) ) // [object Object]
所以我们可以利用 json 格式,做一个中间转化。在 ES5 中引用了 JSON 全局对象,该对象有两个常用的方法:- stringify 方法:将 JavaScript 类型转成对应的 JSON 字符串;- parse 方法:解析 JSON 字符串,转回对应的 JavaScript 类型;<a name="d5ugg"></a>## JSON.stringify( )`JSON.stringify()`方法将一个 JavaScript 对象 或 值 转换为 JSON 字符串。它能接收三个参数。- 注意:**JSON 本质是来存储数据的,而函数是种行为,所以 JSON 不支持函数。**- **所以 stringify 函数会把对象中的函数过滤掉**```javascriptconst obj = {name: "why",age: 18,friends: {name: "kobe"},hobbies: ["篮球", "足球"],sing: function() { // 函数将会被过滤console.log(this.name + ' singing')}}// 需求: 将上面的对象转成JSON字符串// 1.直接转化const jsonString1 = JSON.stringify(obj)console.log(jsonString1)// {"name":"why","age":18,"friends":{"name":"kobe"},"hobbies":["篮球","足球"]}// 2.stringify第二个参数replacer// 2.1. 传入数组: 指定哪些键值对是需要转换const jsonString2 = JSON.stringify(obj, ["name", "friends"])console.log(jsonString2)// {"name":"why","friends":{"name":"kobe"}}// 2.2. 传入回调函数: 可以对字符串进行过滤const jsonString3 = JSON.stringify(obj, (key, value) => {if (key === "age") { // 将 age 的 value 值 +1return value + 1}return value})console.log(jsonString3)// {"name":"why","age":19,"friends":{"name":"kobe"},"hobbies":["篮球","足球"]}// 3.stringify第三个参数 space,对json字符串进行格式化// 数字:表示在键值对前填充空格个数;字符串:填充字符串const jsonString4 = JSON.stringify(obj, null, 2)console.log(jsonString4)// {// "name": "why",// "age": 18,// "friends": {// "name": "kobe"// },// "hobbies": [// "篮球",// "足球"// ]// }
若对象中有toJSON()函数,则stringify()只会将 toJSON 的返回值转成 JSON 字符串。换句话说对象可以使用 toJSON 函数指定转成后的 JSON 字符串。
const obj = {name: "why",age: 18,friends: {name: "kobe"},hobbies: ["篮球", "足球"],toJSON: function() {return "123456"}}// 4.如果obj对象中有toJSON方法const jsonString1 = JSON.stringify(obj)console.log(jsonString1) // "123456"
JSON.parse( )
JSON.parse()方法用来解析 JSON 字符串,构建由字符串描述的 JavaScript 值 或 对象。
const JSONString = '{"name":"why","age":19,"friends":{"name":"kobe"},"hobbies":["篮球","足球"]}'const info1 = JSON.parse(JSONString)console.log(info1)// {// name: 'why',// age: 19,// friends: { name: 'kobe' },// hobbies: [ '篮球', '足球' ]// }// 第二个参数:为可选的 reviver 函数,可以在返回之前对所得到的对象执行变换(操作)const info2 = JSON.parse(JSONString, (key, value) => {if (key === "age") {return value - 1}return value})console.log(info2)// {// name: 'why',// age: 18,// friends: { name: 'kobe' },// hobbies: [ '篮球', '足球' ]// }
使用 JSON 序列化进行深拷贝
引用赋值、浅/深拷贝
有三种常见的复制方式。
- 引用赋值是最普通的一种复制,只是复制了堆内存的地址,两个引用共享一块堆内存。
浅拷贝和深拷贝主要针对于那些存在引用属性的对象,也就是存在引用的引用。
- 浅拷贝就是只复制了那些引用属性保存的引用也就是堆内存的地址。
- 深拷贝就是更深,复制到了引用属性所指向的堆内存上那些内容。
JSON.parse()从字符串还原成对象的时候,内存都是重新开辟的,所以相当于进行了一次深拷贝。但是由于 JSON 的局限性,对象中函数被舍弃了,所以深拷贝了,但没完全深拷贝。
const obj = {name: "why",age: 18,friends: {name: "kobe"},hobbies: ["篮球", "足球"],foo: function() {console.log("foo function")}}// 将obj对象的内容放到info变量中// 1.引用赋值const info = objobj.age = 100 // obj info 指向同一个地址console.log(info.age) // 100// 2.浅拷贝// 可以手动给 info2 动态添加 obj 的属性,也可以使用展开运算符添加const info2 = { ...obj }obj.age = 1000 // 基本属性是真正的拷贝了console.log(info2.age) // 100obj.friends.name = "james" // 引用属性也只是复制了堆内存地址console.log(info2.friends.name) // james// 3.stringify和parse来实现深拷贝const jsonString = JSON.stringify(obj)// JSON 的缺陷,函数被过滤掉了,导致复制不了函数console.log(jsonString)// {"name":"why","age":1000,"friends":{"name":"james"},"hobbies":["篮球","足球"]}const info3 = JSON.parse(jsonString)obj.friends.name = "curry" // 重新开辟了内存,互不影响console.log(info3.friends.name) // james
Storage
WebStorage 主要提供了一种机制,可以让浏览器提供一种比 cookie 更直观的key、value存储方式:
localStorage:本地存储,提供的是一种永久性的存储方法,在关闭掉网页重新打开时,存储的内容依然保留;sessionStorage:会话存储,提供的是本次会话的存储,在关闭掉会话时,存储的内容会被清除;
localStorage 和 sessionStorage 的区别
因为 localStorage 是永久存储,而一个网页窗口就是一个会话,所以关闭网页后重新打开,localStorage 会保留,而 sessionStorage 会被删除。
在页面内实现跳转,并没有新开窗口,所以 localStorage 会保留,sessionStorage 也会保留。在页面外实现跳转(打开新的网页),localStorage 会保留,sessionStorage 不会被保留;
Storage 常见的方法和属性
localStorage 和 sessionStorage 的方法和属性是一样的。
属性:
Storage.length:只读属性- 返回一个整数,表示存储在 Storage 对象中的数据项数量;
方法:
Storage.key():该方法接受一个数值 n 作为参数,返回存储中的第 n 个 key 名称;Storage.getItem():该方法接受一个 key 作为参数,并且返回 key 对应的 value;Storage.setItem():该方法接受一个 key 和 value,并且将会把 key 和 value 添加到存储中。- 如果 key 已经存在,则更新其对应的值;
Storage.removeItem():该方法接受一个key作为参数,并把该key从存储中删除;Storage.clear():该方法的作用是清空存储中的所有key; ```javascript // 1.setItem localStorage.setItem(“name”, “zs”) localStorage.setItem(“age”, 18)
// 2.length console.log(localStorage.length) for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i) console.log(localStorage.getItem(key)) }
// 3.key方法 console.log(localStorage.key(0))
// 4.getItem(key) console.log(localStorage.getItem(“age”))
// 5.removeItem localStorage.removeItem(“age”)
// 6.clear方法 localStorage.clear()
因为两个存储的方法和属性都是一样的,所以可以封装一个工具类,统一使用。```javascriptclass HYCache {constructor(isLocal = true) {this.storage = isLocal ? localStorage: sessionStorage}setItem(key, value) {if (value) {this.storage.setItem(key, JSON.stringify(value))}}getItem(key) {let value = this.storage.getItem(key)if (value) {value = JSON.parse(value)return value}}removeItem(key) {this.storage.removeItem(key)}clear() {this.storage.clear()}key(index) {return this.storage.key(index)}length() {return this.storage.length}}const localCache = new HYCache()const sessionCache = new HYCache(false)export {localCache,sessionCache}
