JavaScript 中的 JSON

先来了解一下几个概念:

JSON 格式

  • JSON 是一种轻量级的、基于文本的、与语言无关的语法,用于定义数据交换格式
  • 它来源于 ECMAScript 编程语言,但是独立于编程语言

对象字面量

  • 是创建对象的一种快捷方式,英文名:object literal
  • 对应还有函数字面量、数组字面量等等
  • 字面量的性能优于使用 new 创建

JSON 特征

  • JSON 就是一串字符串,使用特定的符号标注
  • {} 双括号表示对象
  • [] 中括号表示数组
  • “” 双引号内是属性键或值

JSON 键

  • 只能是字符串
  • 必须用双引号包裹

JSON 值

  • object
  • array
  • number
  • string
  • true
  • false
  • null

举几个合格 JSON 格式的例子:

`["大", "家", "好"]`
`{ "name": "Jae", "age": 23 }`
`{ "Arr": ["1", "2", "3"] }`
`{ "name": null }`
`{}`
`[]`

再看几个不合格 JSON 格式的例子:

`{
  "name": "Jae",
  [Symbol.for("sex")]: 23 // 错误,键只能是字符串,不能是 Symbol
}`

`{
  name: "Jae", // 键必须用双引号包裹,此处没有引号
  'age': 23 // 键必须用双引号包裹,此处用的是单引号
}`

`[-10, 0xDDFF]` // 只允许十进制,这里用了十六进制

`{
  "name": "car",
  "created": new Date(), // 不能有 Date
  "price": 100,
  "getPrice": function() { // 不能有 function
    return this.price
  }
}`

`{
  "name": "Jae",
  "age": 23, // 最后不能有逗号
}`

JSON.parse()

把 JSON 字符串转化为方便使用的对象,这个方法相信大家已经很熟悉了。

这里介绍一下大部分人会忽略的用法,JSON.parse() 的第二个参数:

JSON.parse() 第二个参数 reviver(k, v)

第二个参数为一个函数 reviver(k, v),k 表示当前需要转换的属性键,v 表示当前需要转换的属性值;如果该函数返回 undefined,当前属性会从对象中删除;如果返回其他值,就会成为一个有效值。下面来看看具体的代码示例:

var jsonStr = `{
  "name": "Jae",
  "age": 23,
  "sex": "boy",
  "IdCard": "350204199903256985"
}`
// 隐藏身份证信息
var obj = JSON.parse(jsonStr, function(key, value) {
  if (key === 'IdCard') {
    return undefined;
  } else {
    return value;
  }
});
console.log(obj); // { name: 'Jae', age: 23, sex: 'boy' }

JSON.stringify()

把对象或者基础的值转化为 JSON 字符串的方法,它同样有几个大家容易忽略的参数:

  • 语法:JSON.stringify(value[, replacer [, space]])
  • 第二个参数 replacer:过滤属性或者处理值
  • 第三个参数 space:美化输出格式

JSON.stringify() 第二个参数 replacer

  • 如果该参数是一个函数:则在序列化过程中,被序列化的值的每个属性都会经过该函数的转换和出处理
  • 如果该参数是一个数组:则只有包含在这个数组中的属性名才会被序列化到最终的 JSON 字符串中
  • 如果该参数为 null 或者未提供:则对象所有属性都会被序列化

看一个例子:

var person = {
  name: "Jae",
  age: 23,
  sex: "boy",
  IdCard: "350204199903256985"
};

var jsonString = JSON.stringify(person, function(key, value) {
  if (typeof value === "string") {
    return undefined
  } else {
    return value
  }
});
console.log(jsonString); // '{"age":23}'
console.log(JSON.stringify(person, ['name', 'age'])); // '{"name":"Jae","age":23}'

JSON.stringify() 第三个参数 space

  • 如果参数是数字:它代表有多少的空格;上限为10。该值若小于1,则意味着没有空格
  • 如果该参数为字符串:当字符串长度超过10个字母,取其前10个字母,该字符串将被作为空格
  • 如果该参数为 null 或者未提供:没有空格

看一个例子:

var person = {
  name: "Jae",
  age: 23,
  sex: "boy",
  IdCard: "350204199903256985"
};
console.log(person); // '{"name":"Jae","age":23,"sex":"boy","IdCard":"350204199903256985"}'
console.log(JSON.stringify(person, null, 4));
// 输出为:
// {
//     "name":"Jae",
//     "age":23,
//     "sex":"boy",
//     "IdCard":"350204199903256985"
// }

JSON.stringify() 其他规则

  • 若值为 undefined、函数、Symbol
  1. 作为对象属性值,自动忽略
  2. 作为数组,序列号返回 null
  3. 单独序列化时,返回 undefined
  • Date 返回 ISO 字符串
  • 循环引用报错
  • NaN、Infinity、null 返回 null
  • BigInt 报错
  • Map/Set/WeakMap 等对象,仅序列化可枚举属性

具体看一下代码:

// 作为对象属性值
const data = {
  a: "test1",
  b: undefined,
  c: Symbol("test2"),
  fn: function() {
    return true;
  }
}
console.log(JSON.stringify(data)); // '{"a":"test1"}'

// 作为数组
const data = ["test1", undefined, function aa() {return true}, Symbol("test2")];
console.log(JSON.stringify(data)); // '["test1", null, null, null]'

// 单独序列化时
const a1 = JSON.stringify(function fun() {return true})
const a2 = JSON.stringify(undefined)
const a3 = JSON.stringify(Symbol('test'))
console.log(a1, a2, a3); // undefined undefined undefined

// Date
console.log(JSON.stringify({now: new Date()})); // '{"now":"2022-03-30T06:02:17.348Z"}'

// NaN、Infinity、null
console.log(JSON.stringify(NaN)); // null
console.log(JSON.stringify(Infinity)); // null
console.log(JSON.stringify(null)); // null

// 转换为对应的原始值
console.log(JSON.stringify([new Number(2), new String("test"), new Boolean(false)])); // [2,"test",false]

// 仅序列化可枚举属性
const a = JSON.stringify(
  Object.create(null, {
    test1: { value: 'test1', enumerable: false },
    test2: { value: 'test2', enumerable: true }
  })
); // '{"test2":"test2"}'

// 循环引用报错
const obj = {
  name: "Jae"
};
const obj2 = {
  obj
};
// 对象之间形成循环引用,形成闭环
obj.obj2 = obj2
// 封装一个深拷贝函数
function deepClone(obj) {
  return JSON.parse(JSON.stringify(obj));
}
// 执行深拷贝,抛出错误
deepClone(obj); // Uncaught TypeError: Converting circular structure to JSON

// BigInt 报错
console.log(JSON.stringify(10n)); // Uncaught TypeError: Do not know how to serialize a BigInt

toJSON

总的来说 JSON.stringify() 还是偏复杂的。如果对象拥有 toJSON 方法,toJSON 会覆盖对象默认的序列化行为。看一下代码:

let obj = {
  "name": "Jae",
  "age": 23,
  "school": {
    "address": "xxxxxx",
    "phone": 13306006259
  },
  toJSON() {
    return {
      name: "Jae"
    }
  }
}
console.log(JSON.stringify(obj)); // {"name":"Jae"}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大杯美式不加糖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值