您可能错过的有关 JSON 的一些事项 JSON 的属性 JSON 对象的基本用法 自定义 Stringify 和 Parse 的行为 深度复制对象

2025-06-08

你可能错过的关于 JSON 的一些事情

JSON 的属性

JSON 对象的基本用法

自定义 Stringify 和 Parse 的行为

深度复制对象

立即在http://jauyeung.net/subscribe/订阅我的电子邮件列表

在 Twitter 上关注我:https://twitter.com/AuMayeung

更多文章请访问https://medium.com/@hohanga

JSON 代表 JavaScript 对象表示法。它是一种序列化数据的格式,这意味着它可以用于在不同数据源之间传输和接收数据。在 JavaScript 中,有一个JSON实用程序对象,它提供了将 JavaScript 对象转换为 JSON 字符串以及将 JSON 字符串转换为 JavaScript 对象的函数。该JSON实用程序对象无法被构造或调用——只有 2 个静态方法: 和stringify,用于parse在 JavaScript 对象和 JSON 字符串之间进行转换。

JSON 的属性

JSON 是一种用于序列化对象、数组、数字、布尔值和 的语法null。它基于 JavaScript 对象语法,但它们并不相同。并非所有 JavaScript 对象属性都能转换为有效的 JSON,并且 JSON 字符串必须格式正确才能转换为 JavaScript 对象。

对于对象和数组,JSON 属性名称必须使用双引号括起来的字符串,并且禁止使用对象的尾随逗号。数字不能以零开头,小数点后必须至少跟一位数字。不支持NaNInfinity,JSON 字符串不能包含undefined或 注释。此外,JSON 不能包含函数。

任何 JSON 文本都必须包含有效的 JavaScript 表达式。在某些浏览器引擎中,JSON 中的字符串字面量和属性键允许使用 U+2028 行分隔符和 U+2029 段落分隔符,但在 JavaScript 代码中使用它们会导致 SyntaxError。这两个字符可以用 解析JSON.parse为有效的 JavaScript 字符串,但传入 时会失败eval

除了 JSONNumber 或 JSONString 之外,其他任何地方都可以包含无意义的空格。数字内部不能包含空格,字符串会被解释为字符串中的空格或导致错误。JSON 中唯一有效的空格字符是制表符 (U+0009)、回车符 (U+000D)、换行符 (U+000A) 和空格 (U+0020)。

JSON 对象的基本用法

实用程序对象上有两个方法JSON。一个用于stringify将 JavaScript 对象转换为 JSON 字符串,另一个parse用于将 JSON 字符串转换为 JavaScript 对象。

parse方法将字符串解析为 JSON,并使用一个函数作为第二个参数,该函数可选地将 JSON 实体转换为您指定的 JavaScript 实体,并返回生成的 JavaScript 对象。如果字符串包含 JSON 语法中不允许的实体,则会引发 SyntaxError。此外,传递给 的 JSON 字符串中不允许使用尾部逗号JSON.parse。例如,我们可以像以下代码中一样使用它:

JSON.parse('{}'); // {}       
JSON.parse('false'); // false        
JSON.parse('"abc"'); // 'abc'         
JSON.parse('[1, 5, "abc"]');  // [1, 5, 'abc']  
JSON.parse('null'); // null

第一行将返回一个空对象。第二行将返回false。第三行将返回'abc'。第四行将返回[1, 5, "abc"]。第五行将返回null。由于我们传入的每一行都是有效的 JSON,因此它返回了我们期望的结果。

自定义 Stringify 和 Parse 的行为

或者,我们可以将一个函数作为第二个参数传入,以便将值转换为我们想要的任何值。传入的函数将键作为第一个参数,值作为第二个参数,并在操作完成后返回该值。例如,我们可以这样写:

JSON.parse('{"a:": 1}', (key, value) =>  
  typeof value === 'number'  
    ? value * 10  
    : value       
);

然后我们得到{a: 10}返回值。如果值的类型是数字,则函数返回原始值乘以 10。

JSON.stringify方法可以接受一个函数作为第二个参数,该函数将 JavaScript 对象中的实体映射到 JSON 中的其他内容。默认情况下,所有函数实例undefined和不受支持的原生数据(例如函数)都会被移除。例如,如果我们编写以下代码:

const obj = {  
  fn1() {},  
  foo: 1,  
  bar: 2,  
  abc: 'abc'  
}  
const jsonString = JSON.stringify(obj);  
console.log(jsonString);

然后,我们看到fn1运行后 JSON 字符串中的 会被移除,JSON.stringify因为 JSON 语法不支持函数。对于undefined,我们可以从以下代码中看到undefined属性将被移除。

const obj = {  
  fn1() {},  
  foo: 1,  
  bar: 2,  
  abc: 'abc',  
  nullProp: null,  
  undefinedProp: undefined  
}  
const jsonString = JSON.stringify(obj);  
console.log(jsonString);

undefinedProp不在记录的 JSON 字符串中,因为它已被删除JSON.strinfiy

NaN而且转换成JSON字符串后Infinity都变成:null

const obj = {  
  fn1() {},  
  foo: 1,  
  bar: 2,  
  abc: 'abc',  
  nullProp: null,  
  undefinedProp: undefined,  
  notNum: NaN,  
  infinity: Infinity  
}  
const jsonString = JSON.stringify(obj);  
console.log(jsonString);

我们看到:

'{“foo”:1,”bar”:2,”abc”:”abc”,”nullProp”:null,”notNum”:null,”infinity”:null}'

NaN并且Infinity都取代了null原来的值。

对于不支持的值,我们可以使用 replacer 函数(可选传入第二个参数)将它们映射到支持的值。replace 函数将属性的键作为第一个参数,将值作为第二个参数。例如,保留 、 或 函数的一种方法NaNInfinity将它们映射到字符串,如以下代码所示:

const obj = {  
  fn1() {},  
  foo: 1,  
  bar: 2,  
  abc: 'abc',  
  nullProp: null,  
  undefinedProp: undefined,  
  notNum: NaN,  
  infinity: Infinity  
}

const replacer = (key, value) => {  
  if (value instanceof Function) {  
    return value.toString();  
  } 
  else if (value === NaN) {  
    return 'NaN';  
  } 
  else if (value === Infinity) {  
    return 'Infinity';  
  } 
  else if (typeof value === 'undefined') {  
    return 'undefined';  
  } 
  else {  
    return value; // no change  
  }  
}

const jsonString = JSON.stringify(obj, replacer, 2);  
console.log(jsonString);

console.log在最后一行运行后jsonString,我们看到:

{  
  "fn1": "fn1() {}",  
  "foo": 1,  
  "bar": 2,  
  "abc": "abc",  
  "nullProp": null,  
  "undefinedProp": "undefined",  
  "notNum": null,  
  "infinity": "Infinity"  
}

replace函数的作用是使用 转换的对象中的键和值进行额外的解析JSON.stringify。它会检查 是否为value函数,如果是,则将其转换为字符串并返回。同样,对于NaNInfinityundefined,我们也执行了同样的操作。否则,我们按原样返回值。

该函数的第三个参数JSON.stringfy接受一个数字,用于设置要在 JSON 输出中插入的空格数量,以提高输出的可读性。第三个参数也可以接受任何将代替空格插入的字符串。请注意,如果我们将一个包含空格以外内容的字符串作为第三个参数,则可能会创建一个“JSON”,而这并不是一个有效的 JSON 字符串。

例如,如果我们写:

const obj = {  
  fn1() {},  
  foo: 1,  
  bar: 2,  
  abc: 'abc',  
  nullProp: null,  
  undefinedProp: undefined,  
  notNum: NaN,  
  infinity: Infinity  
}
const replacer = (key, value) => {  
  if (value instanceof Function) {  
    return value.toString();  
  } 
  else if (value === NaN) {  
    return 'NaN';  
  } 
  else if (value === Infinity) {  
    return 'Infinity';  
  } 
  else if (typeof value === 'undefined') {  
    return 'undefined';  
  } 
  else {  
    return value; // no change  
  }  
}
const jsonString = JSON.stringify(obj, replacer, 'abc');  
console.log(jsonString);

然后console.log将是:

{  
abc"fn1": "fn1() {}",  
abc"foo": 1,  
abc"bar": 2,  
abc"abc": "abc",  
abc"nullProp": null,  
abc"undefinedProp": "undefined",  
abc"notNum": null,  
abc"infinity": "Infinity"  
}

这显然不是有效的 JSON。JSON.stringify它会抛出“循环对象值” TypeError。此外,如果对象有BigInt值,转换也会失败,并抛出“BigInt 值无法在 JSON 中序列化” TypeError。

JSON.stringify另外,请注意,如果将符号用作对象的键,则符号会被自动丢弃。因此,如果我们有:

const obj = {  
  fn1() {},  
  foo: 1,  
  bar: 2,  
  abc: 'abc',  
  nullProp: null,  
  undefinedProp: undefined,  
  notNum: NaN,  
  infinity: Infinity,  
  [Symbol('foo')]: 'foo'  
}

const replacer = (key, value) => {
  if (value instanceof Function) {  
    return value.toString();  
  } 
  else if (value === NaN) {  
    return 'NaN';  
  } 
  else if (value === Infinity) {  
    return 'Infinity';  
  } 
  else if (typeof value === 'undefined') {  
    return 'undefined';  
  } 
  else {  
    return value; // no change  
  }  
}

const jsonString = JSON.stringify(obj, replacer, 2);  
console.log(jsonString);

我们得到:

{  
  "fn1": "fn1() {}",  
  "foo": 1,  
  "bar": 2,  
  "abc": "abc",  
  "nullProp": null,  
  "undefinedProp": "undefined",  
  "notNum": null,  
  "infinity": "Infinity"  
}

Date 对象会使用与返回值相同的字符串转换为字符串date.toISOString()。例如,如果我们输入:

const obj = {  
  fn1() {},  
  foo: 1,  
  bar: 2,  
  abc: 'abc',  
  nullProp: null,  
  undefinedProp: undefined,  
  notNum: NaN,  
  infinity: Infinity,  
  [Symbol('foo')]: 'foo',  
  date: new Date(2019, 1, 1)  
}  
const replacer = (key, value) => {  
  if (value instanceof Function) {  
    return value.toString();  
  } 
  else if (value === NaN) {  
    return 'NaN';  
  } 
  else if (value === Infinity) {  
    return 'Infinity';  
  } 
  else if (typeof value === 'undefined') {  
    return 'undefined';  
  } 
  else {  
    return value; // no change  
  }  
}  
const jsonString = JSON.stringify(obj, replacer, 2);  
console.log(jsonString);

我们得到:

{  
  "fn1": "fn1() {}",  
  "foo": 1,  
  "bar": 2,  
  "abc": "abc",  
  "nullProp": null,  
  "undefinedProp": "undefined",  
  "notNum": null,  
  "infinity": "Infinity",  
  "date": "2019-02-01T08:00:00.000Z"  
}

date我们可以看到,转换为 JSON 后,该属性的值现在是一个字符串。

深度复制对象

我们还可以使用JSON.stringifywithJSON.parse来对 JavaScript 对象进行深层复制。例如,要对不带库的对象进行深层复制,你JSON.stringify可以JSON.parse

const a = { foo: {bar: 1, {baz: 2}}  
const b = JSON.parse(JSON.stringfy(a)) // get a clone of a which you can change with out modifying a itself

这会执行对象的深层复制,这意味着对象的所有层级都会被克隆,而不是引用原始对象。之所以能做到这一点,是因为JSON.stringfy将对象转换为不可变的字符串,并且在JSON.parse解析字符串时会返回该对象的副本,从而返回一个不引用原始对象的新对象。

鏂囩珷鏉ユ簮锛�https://dev.to/aumayeung/a-guide-to-json-and-how-it-s-handled-in-javascript-33ae
PREV
让我们构建最小的区块链
NEXT
如何从头构建 graphql api