JSON 的由来

在目前的开发中,JSON是一种非常重要的数据格式,它并不是编程语言,而是一种可以在服务器和客户端之间传输的数据格式。
JSON 的全称是 JavaScript Object Notation(JavaScript对象符号):

  • JSON 是由 Douglas Crockford 构想和设计的一种轻量级资料交换格式,算是 JavaScript 的一个子集;
  • 但是虽然 JSON 被提出来的时候是主要应用 JavaScript 中,但是目前已经独立于编程语言,可以在各个编程语言中使用;
  • 很多编程语言都实现了将 JSON 转成对应模型的方式;

其他的传输格式:

  • XML:在早期的网络传输中主要是使用XML来进行数据交换的,但是这种格式在解析、传输等各方面都弱 JSON,所以目前已经很少在被使用了;
  • Protobuf:另外一个在网络传输中目前已经越来越多使用的传输格式是protobuf,但是直到2021年的3.x版本才支持JavaScript,所以目前在前端使用的较少;

目前JSON被使用的场景也越来越多:

  • 网络数据的传输JSON数据;
  • 项目的某些配置文件;
  • 关系型数据库(NoSQL)将json作为存储格式;

    JSON基本语法

    JSON 的顶层只能有一个,并且支持三种类型的值:
  1. 简单值:数字(Number)、字符串(String,不支持单引号)、布尔类型(Boolean)、null 类型; ```json 123

这样是错误的,json 的顶层只能有一个,json 中也不能添加注释 123 “hhh”

  1. 2. 对象值:由keyvalue组成,key是字符串类型,并且必须添加双引号,值可以是简单值、对象值、数组值;
  2. ```json
  3. {
  4. "name": "zs",
  5. "arr": [1, 2, 3],
  6. "obj": {
  7. "age": 123
  8. } 最后一个键值对不能有逗号结尾
  9. }
  1. 数组值:数组的值可以是简单值、对象值、数组值;
    1. [
    2. 123,
    3. "hhh",
    4. [1, 2, 3],
    5. {
    6. "name": "zs"
    7. }
    8. ]

    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]

  1. 所以我们可以利用 json 格式,做一个中间转化。
  2. ES5 中引用了 JSON 全局对象,该对象有两个常用的方法:
  3. - stringify 方法:将 JavaScript 类型转成对应的 JSON 字符串;
  4. - parse 方法:解析 JSON 字符串,转回对应的 JavaScript 类型;
  5. <a name="d5ugg"></a>
  6. ## JSON.stringify( )
  7. `JSON.stringify()`方法将一个 JavaScript 对象 转换为 JSON 字符串。它能接收三个参数。
  8. - 注意:**JSON 本质是来存储数据的,而函数是种行为,所以 JSON 不支持函数。**
  9. - **所以 stringify 函数会把对象中的函数过滤掉**
  10. ```javascript
  11. const obj = {
  12. name: "why",
  13. age: 18,
  14. friends: {
  15. name: "kobe"
  16. },
  17. hobbies: ["篮球", "足球"],
  18. sing: function() { // 函数将会被过滤
  19. console.log(this.name + ' singing')
  20. }
  21. }
  22. // 需求: 将上面的对象转成JSON字符串
  23. // 1.直接转化
  24. const jsonString1 = JSON.stringify(obj)
  25. console.log(jsonString1)
  26. // {"name":"why","age":18,"friends":{"name":"kobe"},"hobbies":["篮球","足球"]}
  27. // 2.stringify第二个参数replacer
  28. // 2.1. 传入数组: 指定哪些键值对是需要转换
  29. const jsonString2 = JSON.stringify(obj, ["name", "friends"])
  30. console.log(jsonString2)
  31. // {"name":"why","friends":{"name":"kobe"}}
  32. // 2.2. 传入回调函数: 可以对字符串进行过滤
  33. const jsonString3 = JSON.stringify(obj, (key, value) => {
  34. if (key === "age") { // 将 age 的 value 值 +1
  35. return value + 1
  36. }
  37. return value
  38. })
  39. console.log(jsonString3)
  40. // {"name":"why","age":19,"friends":{"name":"kobe"},"hobbies":["篮球","足球"]}
  41. // 3.stringify第三个参数 space,对json字符串进行格式化
  42. // 数字:表示在键值对前填充空格个数;字符串:填充字符串
  43. const jsonString4 = JSON.stringify(obj, null, 2)
  44. console.log(jsonString4)
  45. // {
  46. // "name": "why",
  47. // "age": 18,
  48. // "friends": {
  49. // "name": "kobe"
  50. // },
  51. // "hobbies": [
  52. // "篮球",
  53. // "足球"
  54. // ]
  55. // }

若对象中有toJSON()函数,则stringify()只会将 toJSON 的返回值转成 JSON 字符串。换句话说对象可以使用 toJSON 函数指定转成后的 JSON 字符串。

  1. const obj = {
  2. name: "why",
  3. age: 18,
  4. friends: {
  5. name: "kobe"
  6. },
  7. hobbies: ["篮球", "足球"],
  8. toJSON: function() {
  9. return "123456"
  10. }
  11. }
  12. // 4.如果obj对象中有toJSON方法
  13. const jsonString1 = JSON.stringify(obj)
  14. console.log(jsonString1) // "123456"

JSON.parse( )

JSON.parse()方法用来解析 JSON 字符串,构建由字符串描述的 JavaScript 值 或 对象。

  1. const JSONString = '{"name":"why","age":19,"friends":{"name":"kobe"},"hobbies":["篮球","足球"]}'
  2. const info1 = JSON.parse(JSONString)
  3. console.log(info1)
  4. // {
  5. // name: 'why',
  6. // age: 19,
  7. // friends: { name: 'kobe' },
  8. // hobbies: [ '篮球', '足球' ]
  9. // }
  10. // 第二个参数:为可选的 reviver 函数,可以在返回之前对所得到的对象执行变换(操作)
  11. const info2 = JSON.parse(JSONString, (key, value) => {
  12. if (key === "age") {
  13. return value - 1
  14. }
  15. return value
  16. })
  17. console.log(info2)
  18. // {
  19. // name: 'why',
  20. // age: 18,
  21. // friends: { name: 'kobe' },
  22. // hobbies: [ '篮球', '足球' ]
  23. // }

使用 JSON 序列化进行深拷贝

引用赋值、浅/深拷贝

有三种常见的复制方式。

  1. 引用赋值是最普通的一种复制,只是复制了堆内存的地址,两个引用共享一块堆内存。

浅拷贝和深拷贝主要针对于那些存在引用属性的对象,也就是存在引用的引用。

  1. 浅拷贝就是只复制了那些引用属性保存的引用也就是堆内存的地址。
  2. 深拷贝就是更深,复制到了引用属性所指向的堆内存上那些内容。

JSON.parse()从字符串还原成对象的时候,内存都是重新开辟的,所以相当于进行了一次深拷贝。但是由于 JSON 的局限性,对象中函数被舍弃了,所以深拷贝了,但没完全深拷贝。

  1. const obj = {
  2. name: "why",
  3. age: 18,
  4. friends: {
  5. name: "kobe"
  6. },
  7. hobbies: ["篮球", "足球"],
  8. foo: function() {
  9. console.log("foo function")
  10. }
  11. }
  12. // 将obj对象的内容放到info变量中
  13. // 1.引用赋值
  14. const info = obj
  15. obj.age = 100 // obj info 指向同一个地址
  16. console.log(info.age) // 100
  17. // 2.浅拷贝
  18. // 可以手动给 info2 动态添加 obj 的属性,也可以使用展开运算符添加
  19. const info2 = { ...obj }
  20. obj.age = 1000 // 基本属性是真正的拷贝了
  21. console.log(info2.age) // 100
  22. obj.friends.name = "james" // 引用属性也只是复制了堆内存地址
  23. console.log(info2.friends.name) // james
  24. // 3.stringify和parse来实现深拷贝
  25. const jsonString = JSON.stringify(obj)
  26. // JSON 的缺陷,函数被过滤掉了,导致复制不了函数
  27. console.log(jsonString)
  28. // {"name":"why","age":1000,"friends":{"name":"james"},"hobbies":["篮球","足球"]}
  29. const info3 = JSON.parse(jsonString)
  30. obj.friends.name = "curry" // 重新开辟了内存,互不影响
  31. console.log(info3.friends.name) // james

Storage

WebStorage 主要提供了一种机制,可以让浏览器提供一种比 cookie 更直观的key、value存储方式:

  • localStorage:本地存储,提供的是一种永久性的存储方法,在关闭掉网页重新打开时,存储的内容依然保留;
  • sessionStorage:会话存储,提供的是本次会话的存储,在关闭掉会话时,存储的内容会被清除;

image.png

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()

  1. 因为两个存储的方法和属性都是一样的,所以可以封装一个工具类,统一使用。
  2. ```javascript
  3. class HYCache {
  4. constructor(isLocal = true) {
  5. this.storage = isLocal ? localStorage: sessionStorage
  6. }
  7. setItem(key, value) {
  8. if (value) {
  9. this.storage.setItem(key, JSON.stringify(value))
  10. }
  11. }
  12. getItem(key) {
  13. let value = this.storage.getItem(key)
  14. if (value) {
  15. value = JSON.parse(value)
  16. return value
  17. }
  18. }
  19. removeItem(key) {
  20. this.storage.removeItem(key)
  21. }
  22. clear() {
  23. this.storage.clear()
  24. }
  25. key(index) {
  26. return this.storage.key(index)
  27. }
  28. length() {
  29. return this.storage.length
  30. }
  31. }
  32. const localCache = new HYCache()
  33. const sessionCache = new HYCache(false)
  34. export {
  35. localCache,
  36. sessionCache
  37. }