JSON.stringify & JSON.parse

JSON.stringify & JSON.parse


javascript json 序列化

使用场景

判断对象/数组值是否相等

let a = [1, 2, 3],
  b = [1, 2, 3];
JSON.stringify(a) === JSON.stringify(b); // true

localStorage/sessionStorage 存储对象

localStorage/sessionStorage 只可以存储字符串,当我们想存储对象的时候,需要使用 JSON.stringify 转换成字符串,获取的时候再 JSON.parse

// 存
function setLocalStorage(key, val) {
  window.localStorage.setItem(key, JSON.stringify(val));
}
// 取
function getLocalStorage(key) {
  let val = JSON.parse(window.localStorage.getItem(key));
  return val;
}

实现对象深拷贝

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

const person = {
  name: "Niko",
  age: 26,
};

const copyPerson = deepClone(person);
copyPerson.age = 18;
console.log(person, copyPerson);
// {name: 'Niko', age: 26} {name: 'Niko', age: 18}

路由(浏览器地址)传参

因为浏览器传参只能通过字符串进行,所以也是需要用到 JSON.stringify。

方法使用

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

JSON.parse() 不是只有一个参数,它最多可以有三个参数,只是一般后面两个用不到,所以容易忽略。

JSON.parse(text[, reviver])

JSON.parse 有两个参数,第二个参数跟 JSON.stringify 的第二个参数用处相同(但是只能传函数,不能传数组),所以下文就写一下 JSON.stringify相关的参数。

1. value

将要序列化成 一个 JSON 字符串的值。

2.replacer [可选]

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

2.1 replacer 为函数时

const replacerFun = function (key, value) {
  console.log(key, value);
  //  {name: 'Niko', age: 26}
  // name Niko
  // age 26
  if (key === "name") {
    return undefined;
  }
  return value;
};

const person = {
  name: "Niko",
  age: 26,
};

console.log(JSON.stringify(person, replacerFun));
// {"age":26}

这里其实就是一个筛选的作用,利用的是 JSON.stringify 中对象属性值为 undefined 就会在序列化中被忽略的特性。

值得注意的是,通过观察 for 循环内 log 出来的结果,可以看出在一开始 replacer 函数会被传入一个空字符串作为 key 值,代表着要被 stringify 的这个对象。

2.2 replacer 为数组时

只有包含在这个数组中的属性名才会被序列化

JSON.stringify(person, ["name"]); // '{"name":"Niko"}'

3. space [可选](几乎不会使用)

指定缩进用的空白字符串,用于美化输出(pretty-print);如果参数是个数字,它代表有多少的空格;上限为 10。该值若小于 1,则意味着没有空格;如果该参数为字符串(当字符串长度超过 10 个字母,取其前 10 个字母),该字符串将被作为空格;如果该参数没有提供(或者为 null),将没有空格。

console.log(JSON.stringify(person));
// {"name":"Niko","age":26}
console.log(JSON.stringify(person, null, 2));
/*
{
  "name": "Niko",
  "age": 26
}
*/

注意事项

1. toJSON

转换值如果有 toJSON() 函数,该方法定义什么值将被序列化。该函数返回什么值,序列化结果就是什么值,并且忽略其他属性的值。

// toJSON
const person = {
  name: "Niko",
  age: 26,
  toJSON: function () {
    return "菜鸟";
  },
};

console.log(JSON.stringify(person)); // "菜鸟"

2. 非数组对象的属性不能保证以特定的顺序出现在序列化后的字符串中

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

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

3. 布尔值、数字、字符串的包装对象在序列化过程中会自动转换成对应的原始值。

JSON.stringify([new Number(1), new String("true"), new Boolean(false)]);
//'[1,"true",false]'

4. undefined、函数、symbol 转化

undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。函数、undefined 被单独转换时,会返回 undefined,如JSON.stringify(function(){}) or JSON.stringify(undefined).

4.1 作为对象属性值时:

const data = {
  a: "1",
  b: undefined,
  c: Symbol("c"),
  fn: function () {
    return "function";
  },
};
JSON.stringify(data);
//'{"a":"1"}'

4.2 作为数组元素值时:

const data = ["1", undefined, function c() {}, Symbol("d")];
JSON.stringify(data);
// '["1",null,null,null]'

4.3 单独转换时:

JSON.stringify(function a() {});
// undefined
JSON.stringify(undefined);
// undefined
JSON.stringify(Symbol("symbol"));
// undefined

对于以上情况,我们可以使用 JSON.stringify 的第二个参数,使其达到符合我们的预期。

const data = { x: undefined, y: () => {}, z: Symbol("test") };

const resut = JSON.stringify(data, function (key, value) {
  if (value === undefined) {
    return "undefined";
  } else if (typeof value === "symbol" || typeof value === "function") {
    return value.toString();
  }
  return value;
});

console.log(resut);
// {"x":"undefined","y":"() => {}","z":"Symbol(test)"}

5. 对象之间相互引用,形成无限循环,会抛出错误。

const personA = {
  name: "Niko",
};

const personB = {
  age: 26,
};

personA.age = personB;
personB.name = personA;
JSON.stringify(personA);

报错信息如下:

Uncaught TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'Object'
    |     property 'age' -> object with constructor 'Object'
    --- property 'name' closes the circle
    at JSON.stringify (<anonymous>)
    at <anonymous>:11:6

6. 以 symbol 为属性键的属性

所有以 symbol 为属性键的属性都会被完全忽略掉,即便 replacer 参数中强制指定包含了它们。

JSON.stringify({ [Symbol("test")]: "test" });
// '{}'
JSON.stringify({ [Symbol("test")]: "test" }, function (k, v) {
  if (typeof k === "symbol") {
    return "a symbol";
  }
});
// undefined

使用 toJSON 可以强制转换

const data = {
  [Symbol("test")]: "test",
  toJSON: function () {
    return "toJSON强制转换";
  },
};
JSON.stringify(data);
// '"toJSON强制转换"'

7. Date, RegExp

Date 类型的对象内部有toJSON方法,Date 日期调用了 toJSON() 将其转换为了 string 字符串(同Date.toISOString()),因此会被当做字符串处理。

JSON.stringify(new Date());
// '"2022-10-03T05:59:56.127Z"'
JSON.stringify({ now: new Date() });
// '{"now":"2022-10-03T06:00:30.352Z"}'

RegExp 对象会被转换为空对象

JSON.stringify(new RegExp());
// '{}'
JSON.stringify({ reg: new RegExp() });
// '{"reg":{}}'

8. NaN , Infinitynull

NaNInfinity 格式的数值及 null 都会被当做 null

const person = {
  name: "Niko",
  age: Infinity,
  money: NaN,
};
JSON.stringify(person);
// '{"name":"Niko","age":null,"money":null}'

JSON.stringify([NaN, Infinity]);
// [null,null]
  1. 其他类型的对象,包括 Map/Set/WeakMap/WeakSet,仅会序列化可枚举的属性。
const mapa = new Map();
mapa.set("a", 10);
const mapb = new WeakMap();
mapb.set({ a: 1 }, 10);
const seta = new Set();
seta.add(10);
const setb = new WeakSet();
setb.add({ a: 1 });
JSON.stringify(mapa); // '{}'
JSON.stringify(mapb); // '{}'
JSON.stringify(seta); // '{}'
JSON.stringify(setb); // '{}'
const data = Object.create(null, {
  x: { value: "不能枚举", enumerable: false },
  y: { value: "可以枚举", enumerable: true },
});
JSON.stringify(data); // '{"y":"可以枚举"}'
// 不可枚举的属性被忽略了

//用 replacer 第二参数看下
JSON.stringify(data, (k, v) => {
  console.log("k的值:", k, "v的值:", v);
  return v;
});
// k的值:  v的值: {y: "可以枚举", x: "不能枚举"}
// k的值: y v的值: 可以枚举
// 结果
// "{"y":"可以枚举"}"

参考链接

https://mp.weixin.qq.com/s/KJrFSwvYvvtatqlWvf6xBQ https://blog.csdn.net/guoqiankunmiss/article/details/107837468

© 2025 Niko Xie