JSON.stringify()
JSON.stringify()
静态方法将 JavaScript 值转换为 JSON 字符串,如果指定了替换函数,则可以选择性地替换值,或者如果指定了替换数组,则可以选择性地仅包含指定属性。
试一试
语法
JSON.stringify(value)
JSON.stringify(value, replacer)
JSON.stringify(value, replacer, space)
参数
value
-
要转换为 JSON 字符串的值。
replacer
可选-
一个函数,用于更改字符串化过程的行为,或者一个字符串和数字数组,用于指定输出中要包含的
value
的属性。如果replacer
是一个数组,则此数组中所有不是字符串或数字(基本类型或包装器对象)的元素,包括Symbol
值,都将被完全忽略。如果replacer
不是函数或数组(例如null
或未提供),则对象的全部字符串键属性都将包含在生成的 JSON 字符串中。 space
可选-
一个字符串或数字,用于出于可读性目的将空格(包括缩进、换行符等)插入输出 JSON 字符串中。
如果这是一个数字,则表示要作为缩进使用的空格字符数,限制为 10(也就是说,任何大于
10
的数字都将被视为10
)。小于 1 的值表示不应使用空格。如果这是一个字符串,则该字符串(或如果该字符串超过 10 个字符,则该字符串的前 10 个字符)将插入每个嵌套对象或数组之前。
如果
space
不是字符串或数字(可以是基本类型或包装器对象)——例如,是null
或未提供——则不使用空格。
返回值
表示给定值的 JSON 字符串,或 undefined。
异常
描述
JSON.stringify()
将值转换为该值表示的 JSON 表示法。值将以以下方式进行字符串化
Boolean
、Number
、String
和BigInt
(可通过Object()
获取)对象在字符串化期间将转换为相应的原始值,符合传统的转换语义。Symbol
对象(可通过Object()
获取)将被视为普通对象。- 尝试序列化
BigInt
值将抛出异常。但是,如果 BigInt 有toJSON()
方法(通过猴子补丁:BigInt.prototype.toJSON = ...
),则该方法可以提供序列化结果。此约束确保用户始终明确提供正确的序列化(以及很可能伴随的反序列化)行为。 undefined
、Function
和Symbol
值不是有效的 JSON 值。如果在转换过程中遇到任何此类值,则要么省略它们(在对象中找到时),要么将其更改为null
(在数组中找到时)。JSON.stringify()
在传递“纯”值(如JSON.stringify(() => {})
或JSON.stringify(undefined)
)时可能会返回undefined
。- 数字
Infinity
和NaN
,以及值null
,都被视为null
。(但与前一点中的值不同,它们永远不会被省略。) - 数组被序列化为数组(用方括号括起来)。仅序列化 0 到
length - 1
(含)之间的数组索引;其他属性将被忽略。 - 使用
JSON.rawJSON()
创建的特殊原始 JSON 对象将被序列化为其包含的原始 JSON 文本(通过访问其rawJSON
属性)。 - 对于其他对象
- 所有
Symbol
键属性都将被完全忽略,即使使用replacer
参数也是如此。 - 如果该值具有
toJSON()
方法,则它负责定义将序列化哪些数据。在序列化对象时,将序列化调用toJSON()
方法时返回的值。JSON.stringify()
会使用一个参数(即key
)调用toJSON
,该参数的语义与replacer
函数的key
参数相同- 如果此对象是属性值,则为属性名称
- 如果它位于数组中,则为数组中的索引(作为字符串)
- 如果直接在此对象上调用了
JSON.stringify()
,则为空字符串
Date
对象实现了toJSON()
方法,该方法返回一个字符串(与date.toISOString()
相同)。因此,它们将被字符串化为字符串。 - 仅访问 可枚举的自有属性。这意味着
Map
、Set
等将变为"{}"
。可以使用replacer
参数将其序列化为更有用的内容。属性使用与Object.keys()
相同的算法进行访问,该算法具有明确定义的顺序并且在不同的实现中是稳定的。例如,同一个对象的JSON.stringify
始终会生成相同的字符串,并且JSON.parse(JSON.stringify(obj))
将生成一个与原始对象具有相同键顺序的对象(假设该对象完全可 JSON 序列化)。
- 所有
replacer 参数
replacer
参数可以是函数或数组。
作为数组,其元素指示对象中应包含在生成的 JSON 字符串中的属性的名称。仅考虑字符串和数字值;符号键将被忽略。
作为函数,它接受两个参数:正在字符串化的 key
和 value
。其中找到 key 的对象作为 replacer
的 this
上下文提供。
对于正在字符串化的初始对象,也会调用 replacer
函数,在这种情况下,key
为空字符串 (""
)。然后,它会为正在字符串化的对象或数组上的每个属性调用它。数组索引将以字符串形式作为 key
提供。当前属性值将替换为 replacer
的返回值以进行字符串化。这意味着
- 如果返回数字、字符串、布尔值或
null
,则该值将直接序列化并用作属性的值。(返回 BigInt 也会抛出异常。) - 如果返回
Function
、Symbol
或undefined
,则输出中不包含该属性。 - 如果返回任何其他对象,则会递归地字符串化该对象,并在每个属性上调用
replacer
函数。
注意:使用 replacer
函数生成 JSON 时,可能需要使用 reviver
参数执行反向操作。
通常,数组元素的索引永远不会发生变化(即使元素是无效值(如函数),它也会变为 null
而不是被省略)。使用 replacer
函数可以通过返回不同的数组来控制数组元素的顺序。
space 参数
space
参数可用于控制最终字符串中的空格。
- 如果它是一个数字,则字符串化中的连续级别将分别缩进这么多空格字符。
- 如果它是一个字符串,则连续级别将缩进此字符串。
每个缩进级别永远不会超过 10。space
的数字值限制为 10,字符串值将截断为 10 个字符。
示例
使用 JSON.stringify
JSON.stringify({}); // '{}'
JSON.stringify(true); // 'true'
JSON.stringify("foo"); // '"foo"'
JSON.stringify([1, "false", false]); // '[1,"false",false]'
JSON.stringify([NaN, null, Infinity]); // '[null,null,null]'
JSON.stringify({ x: 5 }); // '{"x":5}'
JSON.stringify(new Date(1906, 0, 2, 15, 4, 5));
// '"1906-01-02T15:04:05.000Z"'
JSON.stringify({ x: 5, y: 6 });
// '{"x":5,"y":6}'
JSON.stringify([new Number(3), new String("false"), new Boolean(false)]);
// '[3,"false",false]'
// String-keyed array elements are not enumerable and make no sense in JSON
const a = ["foo", "bar"];
a["baz"] = "quux"; // a: [ 0: 'foo', 1: 'bar', baz: 'quux' ]
JSON.stringify(a);
// '["foo","bar"]'
JSON.stringify({ x: [10, undefined, function () {}, Symbol("")] });
// '{"x":[10,null,null,null]}'
// Standard data structures
JSON.stringify([
new Set([1]),
new Map([[1, 2]]),
new WeakSet([{ a: 1 }]),
new WeakMap([[{ a: 1 }, 2]]),
]);
// '[{},{},{},{}]'
// TypedArray
JSON.stringify([new Int8Array([1]), new Int16Array([1]), new Int32Array([1])]);
// '[{"0":1},{"0":1},{"0":1}]'
JSON.stringify([
new Uint8Array([1]),
new Uint8ClampedArray([1]),
new Uint16Array([1]),
new Uint32Array([1]),
]);
// '[{"0":1},{"0":1},{"0":1},{"0":1}]'
JSON.stringify([new Float32Array([1]), new Float64Array([1])]);
// '[{"0":1},{"0":1}]'
// toJSON()
JSON.stringify({
x: 5,
y: 6,
toJSON() {
return this.x + this.y;
},
});
// '11'
// Symbols:
JSON.stringify({ x: undefined, y: Object, z: Symbol("") });
// '{}'
JSON.stringify({ [Symbol("foo")]: "foo" });
// '{}'
JSON.stringify({ [Symbol.for("foo")]: "foo" }, [Symbol.for("foo")]);
// '{}'
JSON.stringify({ [Symbol.for("foo")]: "foo" }, (k, v) => {
if (typeof k === "symbol") {
return "a symbol";
}
});
// undefined
// Non-enumerable properties:
JSON.stringify(
Object.create(null, {
x: { value: "x", enumerable: false },
y: { value: "y", enumerable: true },
}),
);
// '{"y":"y"}'
// BigInt values throw
JSON.stringify({ x: 2n });
// TypeError: BigInt value can't be serialized in JSON
使用函数作为 replacer
function replacer(key, value) {
// Filtering out properties
if (typeof value === "string") {
return undefined;
}
return value;
}
const foo = {
foundation: "Mozilla",
model: "box",
week: 45,
transport: "car",
month: 7,
};
JSON.stringify(foo, replacer);
// '{"week":45,"month":7}'
如果您希望replacer
能够区分初始对象和键为空字符串的属性(因为两者都会将空字符串作为键,并可能将对象作为值),则需要跟踪迭代次数(如果迭代次数超过第一次,则表示这是一个真正的空字符串键)。
function makeReplacer() {
let isInitial = true;
return (key, value) => {
if (isInitial) {
isInitial = false;
return value;
}
if (key === "") {
// Omit all properties with name "" (except the initial object)
return undefined;
}
return value;
};
}
const replacer = makeReplacer();
console.log(JSON.stringify({ "": 1, b: 2 }, replacer)); // "{"b":2}"
使用数组作为replacer
const foo = {
foundation: "Mozilla",
model: "box",
week: 45,
transport: "car",
month: 7,
};
JSON.stringify(foo, ["week", "month"]);
// '{"week":45,"month":7}', only keep "week" and "month" properties
使用space参数
使用一个空格缩进输出
console.log(JSON.stringify({ a: 2 }, null, " "));
/*
{
"a": 2
}
*/
使用制表符可以模拟标准的漂亮打印外观
console.log(JSON.stringify({ uno: 1, dos: 2 }, null, "\t"));
/*
{
"uno": 1,
"dos": 2
}
*/
toJSON()行为
为对象定义toJSON()
允许覆盖其序列化行为。
const obj = {
data: "data",
toJSON(key) {
return key ? `Now I am a nested object under key '${key}'` : this;
},
};
JSON.stringify(obj);
// '{"data":"data"}'
JSON.stringify({ obj });
// '{"obj":"Now I am a nested object under key 'obj'"}'
JSON.stringify([obj]);
// '["Now I am a nested object under key '0'"]'
序列化循环引用问题
由于JSON格式不支持对象引用(尽管存在IETF草案),因此如果尝试编码包含循环引用的对象,则会抛出TypeError
。
const circularReference = {};
circularReference.myself = circularReference;
// Serializing circular references throws "TypeError: cyclic object value"
JSON.stringify(circularReference);
要序列化循环引用,您可以使用支持它们的库(例如,Douglas Crockford 的cycle.js)或自己实现一个解决方案,这将需要找到并替换(或删除)可序列化值的循环引用。
如果您正在使用JSON.stringify()
深度复制对象,则可能需要改用structuredClone()
,它支持循环引用。JavaScript 引擎用于二进制序列化的 API,例如v8.serialize()
,也支持循环引用。
将JSON.stringify()与localStorage一起使用
在您希望存储用户创建的对象并允许在浏览器关闭后恢复的情况下,以下示例是JSON.stringify()
适用性的模型
// Creating an example of JSON
const session = {
screens: [],
state: true,
};
session.screens.push({ name: "screenA", width: 450, height: 250 });
session.screens.push({ name: "screenB", width: 650, height: 350 });
session.screens.push({ name: "screenC", width: 750, height: 120 });
session.screens.push({ name: "screenD", width: 250, height: 60 });
session.screens.push({ name: "screenE", width: 390, height: 120 });
session.screens.push({ name: "screenF", width: 1240, height: 650 });
// Converting the JSON string with JSON.stringify()
// then saving with localStorage in the name of session
localStorage.setItem("session", JSON.stringify(session));
// Example of how to transform the String generated through
// JSON.stringify() and saved in localStorage in JSON object again
const restoredSession = JSON.parse(localStorage.getItem("session"));
// Now restoredSession variable contains the object that was saved
// in localStorage
console.log(restoredSession);
格式良好的JSON.stringify()
实现格式良好的JSON.stringify规范的引擎将使用Unicode转义序列而不是字面量来序列化孤立的代理(任何从U+D800到U+DFFF的代码点)(输出孤立的代理)。在此更改之前,此类字符串无法以有效的UTF-8或UTF-16进行编码
JSON.stringify("\uD800"); // '"�"'
但通过此更改,JSON.stringify()
使用JSON转义序列表示孤立的代理,这些序列可以以有效的UTF-8或UTF-16进行编码
JSON.stringify("\uD800"); // '"\\ud800"'
只要您将JSON.stringify()
的结果传递给诸如JSON.parse()
之类的API(这些API将接受任何有效的JSON文本),因为它们将把孤立代理的Unicode转义视为与孤立代理本身相同,此更改应该向后兼容。只有当您直接解释JSON.stringify()
的结果时,您才需要仔细处理JSON.stringify()
对这些代码点的两种可能的编码。
规范
规范 |
---|
ECMAScript语言规范 # sec-json.stringify |
浏览器兼容性
BCD表仅在浏览器中加载