# 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 的顶层支持三种类型的值:

    • 简单值:数字(Number)、字符串(String,不支持单引号)、布尔类型(Boolean)、null 类型;
"nekoaimer"
  • 对象值:由 key、value 组成,key 是字符串类型,并且必须添加双引号,值可以是简单值、对象值、数组值;
{
  "name": "Lain",
  "age": 16,
  "friends": {
    "name": "Saber",
    "age": 17
  }
}
  • 数组值:数组的值可以是简单值、对象值、数组值;
[
  "nekoaimer",
  16,
  {
    "name": "Lain",
    "age": 16,
    "friends": {
      "name": "Saber",
      "age": 17
    }
  }
]

# JSON 序列化

  • 某些情况下我们希望将 JavaScript 中的复杂类型转化成 JSON 格式的字符串,这样方便对其进行处理:
    • 比如我们希望将一个对象保存到 localStorage 中;
    • 但是如果我们直接存放一个对象,这个对象会被转化成 [object Object] 格式的字符串,并不是我们想要的结果;

# JSON 序列化方法

  • 在 ES5 中引用了 JSON 全局对象,该对象有两个常用的方法:
    • stringify 方法:将 JavaScript 类型转成对应的 JSON 字符串;
    • parse 方法:解析 JSON 字符串,转回对应的 JavaScript 类型;
  • 那么上面的代码我们可以通过如下的方法来使用:
const lain = {
  name: "lain",
  age: 16,
  friends: {
    name: "saber"
  }
}
// 将 lain 转成 JSON 格式的字符串
const lainString = JSON.stringify(lain)
console.log(lainString) // {"name":"lain","age":16,"friends":{"name":"saber"}}
// 将对象数据存储 localStorage
localStorage.setItem("lain", lainString)
// 获取 localStorage 中存放的 JSON 数据
const jsonString = localStorage.getItem("lain")
// 再将 JSON 格式的字符串转回对象
const lainObj = JSON.parse(jsonString)
console.log(lainObj) // {name: 'lain', age: 16, friends: {…}}

# Stringify 的参数 replace

  • JSON.stringify () 方法将一个 JavaScript 对象或值转换为 JSON 字符串:
    • 如果指定了一个 replacer 函数,则可以选择性地替换值;
    • 如果指定的 replacer 是数组,则可选择性地仅包含数组指定的属性;
  • 下面以 lain 对象举栗
const lain = {
  name: "lain",
  age: 16,
  friends: {
    name: "saber"
  }
}

# 直接转化

  • 普通用法 传入一个对象参数
const jsonString1 = JSON.stringify(lain)
console.log(jsonString1) // {"name":"lain","age":16,"friends":{"name":"saber"}}

# 传入数组

  • 设定哪些是需要转换
const jsonString2 = JSON.stringify(lain, ["name", "age"])
console.log(jsonString2) // {"name":"lain","age":16}

# 传入回调函数

  • key 是每一个字段
  • value 是每个字段的值
  • 例如下面打印 key 就是 name age friends name
  • 例如下面打印 value 就是 lain 16 {name: 'saber'} saber
const jsonString3 = JSON.stringify(lain, (key, value) => {
  console.log(key)
  console.log(value)
  if (key === "age") return ++value
  return value
})
console.log(jsonString3) // {"name":"lain","age":17,"friends":{"name":"saber"}}

# stringify 第三参数 space

  • 当是数字时,会在前面加上 4 个空格
const jsonString4 = JSON.stringify(lain, null, 4)
console.log(jsonString4)  
/* 
{
    "name": "lain",
    "age": 16,
    "friends": {
        "name": "saber"
    }
}
*/
  • 当是字符时,则会在前面加上字符
const jsonString4 = JSON.stringify(lain, null, '--')
console.log(jsonString4)  
/*
{
--"name": "lain",
--"age": 16,
--"friends": {
----"name": "saber"
--}
}
*/

# 如果对象中有 toJSON 方法

  • 如果转化的对象包含了 toJSON 函数时,它的返回值就会作为结果
const lain = {
  name: "lain",
  age: 16,
  friends: {
    name: "saber"
  },
  toJSON: () => '我将是结果'
}
const jsonString1 = JSON.stringify(lain)
console.log(jsonString1) // "我将是结果"
const jsonString2 = JSON.stringify(lain, null, 123)
console.log(jsonString2) // "我将是结果"
const jsonString3 = JSON.stringify(lain, null, '--')
console.log(jsonString3) // "我将是结果"

# parse 方法

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

    • 提供可选的 reviver 函数用以在返回之前对所得到的对象执行变换 (操作)
const JSONString = '{"name":"lain","age":16}'
// 对返回的转化过程做了一个拦截
const info = JSON.parse(JSONString, (key, value) => {
  if (key === "age") return --value
  return value
})
console.log(info) // {name: 'lain', age: 15}

# 使用 JSON 序列化深拷贝

  • 下面以 lain 对象举栗
const lain = {
  name: "lain",
  age: 16,
  friends: {
    name: "saber"
  },
  foo() {
    console.log('foo~')
  }
}

# 引用赋值

const lain2 = lain
lain.age = 17
console.log(lain2.age) // 17
console.log(lain.age) // 17

# 浅拷贝

  • 展开运算发相当于只是将 lain 对象中 keyvalue 复制了一份给新的 lain2 对象里面
  • 比如 lain 在内存地址是 0xa00 , lain2oxb00 , 也就是说 lainlain2 是在内存里面是两个对象
const lain2 = { ...lain }
lain.age = 17
console.log(lain.age) // 17
console.log(lain2.age) // 16
  • 但里面的 friends 属性依然指向的是同一个对象,所以这也是浅拷贝
const lain2 = { ...lain }
lain.friends.name = "nekoaimer"
console.log(lain.friends.name) // nekoaimer
console.log(lain2.friends.name) // nekoaimer

# stringify 和 parse 来实现深拷贝

  • 利用 stringify 和 parse 来实现深拷贝,但是也是有问题的
const jsonString = JSON.stringify(lain)
console.log(jsonString) // {"name":"lain","age":16,"friends":{"name":"saber"}}
const lain2 = JSON.parse(jsonString)
lain.friends.name = "nekoaimer"
console.log(lain.friends.name) // nekoaimer
console.log(lain2.friends.name) // saber
  • 上面说的问题细心的同学已经发现了问题,上面 lain 对象中是有一个 foo 函数的,但上面实现深拷贝转化 JSON 格式打印时却没有这个函数
const lain = {
  name: "lain",
  age: 16,
  friends: {
    name: "saber"
  },
  foo() {
    console.log('foo~')
  }
}
const jsonString = JSON.stringify(lain)
console.log(jsonString) // {"name":"lain","age":16,"friends":{"name":"saber"}}
  • 所以我们能够知道 stringify和parse确实可以实现深拷贝 ,但是对于 函数 是无能为力的~
Update on Views times

Give me a cup of [coffee]~( ̄▽ ̄)~*

Nico Niconi WeChat Pay

WeChat Pay

Nico Niconi Alipay

Alipay