JS Advance --- JSON

273 阅读4分钟

JSON由来

这是我参与11月更文挑战的第9天,活动详情查看:2021最后一次更文挑战

在目前的开发中,JSON是一种非常重要的数据格式,它并不是编程语言,而是一种可以在服务器和客户端之间传输的数据格式。

JSON的全称是JavaScript Object Notation(JavaScript对象符号):

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

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

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

基本写法

JSON的顶层支持三种类型的值:

  • 简单值:数字(Number)、字符串(String,不支持单引号)、布尔类型(Boolean)、null类型(JSON不支持undefined

  • 对象值:由key、value组成,key是字符串类型,并且必须添加双引号,值可以是简单值、对象值、数组值(JSON不支持函数

  • 数组值:数组的值可以是简单值、对象值、数组值

  • JSON中不支持任何形式的注释

  • JSON的数据项如果是最后一个,那么该数据项不可以以逗号进行结尾

[
  null,
  {
    "name": "Klaus",
    "age": 23
  }
]

序列化

某些情况下我们希望将JavaScript中的复杂类型转化成JSON格式的字符串,这样方便对其进行处理

比如我们希望将一个对象保存到localStorage中,但是如果我们直接存放一个对象,这个对象会被转化成 [object Object] 格式的字符串,并不是我们想要的结果

在ES5中引用了JSON全局对象,该对象有两个常用的方法:

  • stringify方法:将JavaScript类型转成对应的JSON字符串
  • parse方法:解析JSON字符串,转回对应的JavaScript类型
const user = {
  name: 'Klaus',
  age: 23
}

// obj -> JSON string--- 序列化
const str = JSON.stringify(user)
console.log(str)

// JSON string -> obj ---- 反序列化(解析)
const obj = JSON.parse(str)
console.log(obj)

JSON方法

stringify

基本使用

const user = {
  name: 'Klaus',
  age: 23,
  friend: {
    name: 'Alex',
    age: 18
  }
}

const str = JSON.stringify(user)
console.log(str)

第二个参数 --- replacer --- 选择性的替换值

const user = {
  name: 'Klaus',
  age: 23,
  friend: {
    name: 'Alex',
    age: 18
  }
}

// 类型1 --- 数组
// 转换后的结果,只要name和age字段,不要friend字段
const str1 = JSON.stringify(user, ['name', 'age'])

console.log(str1) // => {"name":"Klaus","age":23}

// 类型2 --- 函数
// 可以拦截每一个字段,进行自行处理
const str2 = JSON.stringify(user, (key, value) => {
  // 如果不指定这个函数,默认值就是直接返回value
  // return value
  if (key === 'age') {
    value += 1
  }

  return value
})

console.log(str2) // => {"name":"Klaus","age":24,"friend":{"name":"Alex","age":18}}

参数3 --- space --- 需要添加的空格数

const user = {
  name: 'Klaus',
  age: 23,
  friend: {
    name: 'Alex',
    age: 18
  }
}

// 类型1 --- 数字 --- 假设值为n
// 转换后的结果换行,且每个字段前添加n个空格

// 第二个参数如果传递的是null,那么表示的就是使用默认值
const str1 = JSON.stringify(user, null, 2)
console.log(str1)
/*
  =>
    {
      "name": "Klaus",
      "age": 23,
      "friend": {
        "name": "Alex",
        "age": 18
      }
    }
*/

// 类型2 --- 字符串
// 转换后的结果换行,且每个字段前添加传入的字符串
const str2 = JSON.stringify(user, null, '---')
console.log(str2)
/*
  =>
    {
      ---"name": "Klaus",
      ---"age": 23,
      ---"friend": {
      ------"name": "Alex",
      ------"age": 18
      ---}
    }
*/

toJSON

const user = {
  name: 'Klaus',
  age: 23,
  friend: {
    name: 'Alex',
    age: 18
  },
  toJSON() {
    // 如果一个对象中存在toJSON方法
    // 那么在将这个对象转换为JSON字符串的时候
    // 会自动去调用该toJSON方法
    return '调用了user的toJSON方法'
  }
}

console.log(JSON.stringify(user)) // => "调用了user的toJSON方法"

parse

const str = '{"name":"Klaus","age":24,"friend":{"name":"Alex","age":18}}'

// 1. 基本使用
console.log(JSON.parse(str))

// 2. 传入第二个参数 --- 拦截,进行自定义操作
console.log(JSON.parse(str, (key, value) => {
  if (key === 'age') {
    value -= 1
  }
  return value
}))

JSON.parse方法返回的对象并不是之前的对象,实际上是对之前的对象进行了一次深拷贝

但是因为JSON并不支持函数这种数据格式,所以使用JSON序列化的方式来进行深拷贝的时候,如果遇到函数的时候是无能为力的

const user = {
  name: 'Klaus',
  age: 23,
  friend: {
    name: 'Alex',
    age: 18
  },
  fun() {
    console.log('我并不能被深拷贝')
  }
}

const newUser = JSON.parse(JSON.stringify(user))

user.friend.name = 'Jhon'
console.log(newUser.friend.name) // => Alex

// 并不存在fun函数
console.log(newUser) // => { name: 'Klaus', age: 23, friend: { name: 'Alex', age: 18 } }