Skip to content
imshengli blog
Go back

JSON 深入:parse/stringify 高级用法与常见陷阱

JSON 深入解析:语法规则、parse/stringify 高级用法、与 JS 对象的区别、深拷贝应用与常见陷阱。

· 4 min

简介

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,用来序列化对象、数组、数值、字符串、布尔值和 null。

JSON 语法规则

// 合法 JSON
{ "name": "Tom", "age": 20 }

// 非法 JSON
{ name: "Tom" }          // 键名没有双引号
{ "name": 'Tom' }        // 值用了单引号
{ "name": "Tom", }       // 末尾逗号

JSON 只支持字符串、数值、布尔、null、对象和数组这几种值类型,不支持 JavaScript 中的函数、undefined、Symbol、Date 等。

JSON.stringify()

将一个 JavaScript 值转换为 JSON 字符串。

JSON.stringify(value[, replacer[, space]])

基本转换规则:

JSON.stringify({});                        // '{}'
JSON.stringify(true);                      // 'true'
JSON.stringify([1, "false", false]);       // '[1,"false",false]'

JSON.stringify({x: undefined, y: Object, z: Symbol("")});
// '{}'

JSON.stringify([undefined, Object, Symbol("")]);
// '[null,null,null]'

JSON.stringify(NaN);          // 'null'
JSON.stringify(new Date());   // '"2018-01-11T00:00:00.000Z"'

replacer 参数

replacer 为函数时,每个属性都经过该函数处理;为数组时,只序列化指定的属性名。

function replacer(key, value) {
  if (typeof value === "string") {
    return undefined;
  }
  return value;
}

var foo = { foundation: "Mozilla", model: "box", week: 45, month: 7 };

JSON.stringify(foo, replacer);
// '{"week":45,"month":7}'

JSON.stringify(foo, ['week', 'month']);
// '{"week":45,"month":7}'

space 参数

用于美化输出,可以是数字(缩进空格数)或字符串。

var obj = { a: 1, b: { c: 2 } };

JSON.stringify(obj, null, 2);
// '{
//   "a": 1,
//   "b": {
//     "c": 2
//   }
// }'

toJSON 方法

如果对象定义了 toJSON 方法,序列化时会调用该方法的返回值。

var obj = {
  foo: 'foo',
  toJSON: function () { return 'bar'; }
};
JSON.stringify(obj);      // '"bar"'
JSON.stringify({x: obj}); // '{"x":"bar"}'

JSON.parse()

将 JSON 字符串解析为 JavaScript 值。

JSON.parse(text[, reviver])

reviver 参数

reviver 函数用于在返回之前对解析的值进行转换。遍历顺序从内到外,最后处理顶层对象(key 为空字符串)。

JSON.parse('{"p": 5}', function (k, v) {
  if (k === '') return v;
  return v * 2;
});
// { p: 10 }

// 日期字符串还原为 Date 对象
var str = '{"date":"2018-01-11T00:00:00.000Z","name":"test"}';
JSON.parse(str, function (key, value) {
  if (key === 'date') return new Date(value);
  return value;
});

深拷贝应用

利用 JSON 序列化和反序列化实现深拷贝:

function deepClone(obj) {
  return JSON.parse(JSON.stringify(obj));
}

var original = { a: 1, b: { c: 2 }, d: [1, 2, 3] };
var copy = deepClone(original);
copy.b.c = 99;
original.b.c; // 2,不受影响

局限性——以下类型无法正确处理:

var obj = {
  fn: function() {},       // 函数丢失
  undef: undefined,        // undefined 丢失
  date: new Date(),        // Date 变成字符串
  reg: /hello/g,           // 正则变成空对象 {}
  nan: NaN,                // NaN 变成 null
  inf: Infinity            // Infinity 变成 null
};
JSON.parse(JSON.stringify(obj));
// { date: "2018-01-11T...", reg: {}, nan: null, inf: null }

常见陷阱

循环引用

var a = {};
a.self = a;
JSON.stringify(a); // TypeError: Converting circular structure to JSON

特殊值

JSON.stringify(BigInt(1));             // TypeError
JSON.stringify(new Map([['a', 1]]));   // '{}'
JSON.stringify(new Set([1, 2, 3]));    // '{}'

parse 对格式严格

JSON.parse("'hello'");      // SyntaxError,必须双引号
JSON.parse("{a: 1}");       // SyntaxError,键名必须双引号
JSON.parse("undefined");    // SyntaxError,undefined 不是合法 JSON

参考链接


Share this post on:

Previous Post
ES6 函数:箭头函数、默认参数与尾调用优化
Next Post
Web 安全基础:XSS 与 CSRF 攻击原理及防御