JSON.stringify()

JSON.stringify() 静态方法将 JavaScript 值转换为 JSON 字符串,如果指定了替换函数,则可选择替换值;如果指定了替换数组,则可选择仅包含指定的属性。

¥The JSON.stringify() static method converts a JavaScript value to a JSON string, optionally replacing values if a replacer function is specified or optionally including only the specified properties if a replacer array is specified.

Try it

语法

¥Syntax

js
JSON.stringify(value)
JSON.stringify(value, replacer)
JSON.stringify(value, replacer, space)

参数

¥Parameters

value

要转换为 JSON 字符串的值。

replacer Optional

改变字符串化过程行为的函数,或指定要包含在输出中的 value 属性的字符串和数字数组。如果 replacer 是一个数组,则该数组中所有非字符串或数字(基元或封装对象)的元素(包括 Symbol 值)都将被完全忽略。如果 replacer 不是函数或数组(例如 null 或未提供),则对象的所有字符串键控属性都将包含在生成的 JSON 字符串中。

space Optional

一个字符串或数字,用于在输出 JSON 字符串中插入空格(包括缩进、换行符等)以提高可读性。

如果这是一个数字,则表示用作缩进的空格字符数,限制为 10(也就是说,任何大于 10 的数字都将被视为 10)。小于 1 的值表示不应使用空间。

如果这是一个字符串,则该字符串(或该字符串的前 10 个字符,如果它比该字符长)将插入到每个嵌套对象或数组之前。

如果 space 不是字符串或数字(可以是基元或封装对象),例如,是 null 或未提供,则不使用空格。

返回值

¥Return value

表示给定值或未定义的 JSON 字符串。

¥A JSON string representing the given value, or undefined.

例外情况

¥Exceptions

TypeError

有下列情况之一的,抛出:

  • value 包含循环引用。
  • 遇到 BigInt 值。

描述

¥Description

JSON.stringify() 将值转换为该值表示的 JSON 表示法。值按以下方式字符串化:

¥JSON.stringify() converts a value to the JSON notation that the value represents. Values are stringified in the following manner:

  • BooleanNumberStringBigInt(可通过 Object() 获得)对象在字符串化期间根据传统的转换语义转换为相应的原始值。Symbol 对象(可通过 Object() 获得)被视为普通对象。
  • 尝试序列化 BigInt 值将会抛出异常。但是,如果 BigInt 有 toJSON() 方法(通过猴子修补:BigInt.prototype.toJSON = ...),则该方法可以提供序列化结果。此约束确保用户始终显式提供正确的序列化(以及很可能伴随的反序列化)行为。
  • undefinedFunctionSymbol 值不是有效的 JSON 值。如果在转换过程中遇到任何此类值,它们将被省略(在对象中找到时)或更改为 null(在数组中找到时)。当传入 JSON.stringify(() => {})JSON.stringify(undefined) 等 "pure" 值时,JSON.stringify() 可以返回 undefined
  • 数字 InfinityNaN 以及值 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() 相同)。因此,它们将被字符串化为字符串。
    • 仅访问了 可枚举自己的属性 个。这意味着 MapSet 等将变成 "{}"。你可以使用 replacer 参数将它们序列化为更有用的内容。 使用与 Object.keys() 相同的算法来访问属性,该算法具有明确定义的顺序并且在不同实现中保持稳定。例如,同一对象上的 JSON.stringify 将始终生成相同的字符串,而 JSON.parse(JSON.stringify(obj)) 将生成与原始对象具有相同键顺序的对象(假设该对象完全可 JSON 序列化)。

替换参数

¥The replacer parameter

replacer 参数可以是函数或数组。

¥The replacer parameter can be either a function or an array.

作为数组,其元素指示对象中应包含在生成的 JSON 字符串中的属性名称。仅考虑字符串和数字值;符号键被忽略。

¥As an array, its elements indicate the names of the properties in the object that should be included in the resulting JSON string. Only string and number values are taken into account; symbol keys are ignored.

作为一个函数,它有两个参数:keyvalue 被字符串化。找到密钥的对象作为 replacerthis 上下文提供。

¥As a function, it takes two parameters: the key and the value being stringified. The object in which the key was found is provided as the replacer's this context.

还为要字符串化的初始对象调用 replacer 函数,在这种情况下 key 是空字符串 ("")。然后为要字符串化的对象或数组上的每个属性调用它。数组索引将以字符串形式提供,如 key。当前属性值将替换为 replacer 的返回值以进行字符串化。这意味着:

¥The replacer function is called for the initial object being stringified as well, in which case the key is an empty string (""). It is then called for each property on the object or array being stringified. Array indices will be provided in its string form as key. The current property value will be replaced with the replacer's return value for stringification. This means:

  • 如果返回数字、字符串、布尔值或 null,则该值将直接序列化并用作属性的值。(返回 BigInt 也会抛出异常。)
  • 如果返回 FunctionSymbolundefined,则该属性不会包含在输出中。
  • 如果返回任何其他对象,该对象将被递归字符串化,对每个属性调用 replacer 函数。

注意:当解析使用 replacer 函数生成的 JSON 时,你可能希望使用 reviver 参数来执行相反的操作。

¥Note: When parsing JSON generated with replacer functions, you would likely want to use the reviver parameter to perform the reverse operation.

通常,数组元素的索引永远不会移动(即使该元素是像函数一样的无效值,它也会变成 null 而不是被省略)。使用 replacer 函数允许你通过返回不同的数组来控制数组元素的顺序。

¥Typically, array elements' index would never shift (even when the element is an invalid value like a function, it will become null instead of omitted). Using the replacer function allows you to control the order of the array elements by returning a different array.

空间参数

¥The space parameter

space 参数可用于控制最终字符串中的间距。

¥The space parameter may be used to control spacing in the final string.

  • 如果它是数字,则字符串化中的连续级别将按这么多空格字符缩进。
  • 如果它是一个字符串,则连续的级别将按该字符串缩进。

每级缩进永远不会超过 10 级。space 的数值被限制为 10,字符串值被截断为 10 个字符。

¥Each level of indentation will never be longer than 10. Number values of space are clamped to 10, and string values are truncated to 10 characters.

示例

¥Examples

使用 JSON.stringify

¥Using JSON.stringify

js
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

使用函数作为替换器

¥Using a function as replacer

js
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 区分初始对象和具有空字符串属性的键(因为两者都会将空字符串作为键,并可能将对象作为值),你将必须跟踪迭代计数(如果是) 除了第一次迭代之外,它是一个真正的空字符串键)。

¥If you wish the replacer to distinguish an initial object from a key with an empty string property (since both would give the empty string as key and potentially an object as value), you will have to keep track of the iteration count (if it is beyond the first iteration, it is a genuine empty string key).

js
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}"

使用数组作为替换

¥Using an array as replacer

js
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

使用空间参数

¥Using the space parameter

将输出缩进一个空格:

¥Indent the output with one space:

js
console.log(JSON.stringify({ a: 2 }, null, " "));
/*
{
 "a": 2
}
*/

使用制表符模仿标准的漂亮打印外观:

¥Using a tab character mimics standard pretty-print appearance:

js
console.log(JSON.stringify({ uno: 1, dos: 2 }, null, "\t"));
/*
{
	"uno": 1,
	"dos": 2
}
*/

toJSON() 行为

¥toJSON() behavior

为对象定义 toJSON() 允许覆盖其序列化行为。

¥Defining toJSON() for an object allows overriding its serialization behavior.

js
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'"]'

序列化循环引用的问题

¥Issue with serializing circular references

由于 JSON 格式 不支持对象引用(尽管有 IETF 草案已存在),因此如果尝试使用循环引用对对象进行编码,则会抛出 TypeError

¥Since the JSON format doesn't support object references (although an IETF draft exists), a TypeError will be thrown if one attempts to encode an object with circular references.

js
const circularReference = {};
circularReference.myself = circularReference;

// Serializing circular references throws "TypeError: cyclic object value"
JSON.stringify(circularReference);

要序列化循环引用,你可以使用支持它们的库(例如 Douglas Crockford 的 cycle.js)或自己实现解决方案,这需要通过可序列化的值查找和替换(或删除)循环引用。

¥To serialize circular references, you can use a library that supports them (e.g. cycle.js by Douglas Crockford) or implement a solution yourself, which will require finding and replacing (or removing) the cyclic references by serializable values.

如果你使用 JSON.stringify() 深度复制对象,你可能需要使用 structuredClone(),它支持循环引用。用于二进制序列化的 JavaScript 引擎 API(例如 v8.serialize())也支持循环引用。

¥If you are using JSON.stringify() to deep-copy an object, you may instead want to use structuredClone(), which supports circular references. JavaScript engine APIs for binary serialization, such as v8.serialize(), also support circular references.

将 JSON.stringify() 与 localStorage 结合使用

¥Using JSON.stringify() with localStorage

如果你想要存储用户创建的对象并允许在浏览器关闭后恢复它,以下示例是 JSON.stringify() 适用性的模型:

¥In a case where you want to store an object created by your user and allow it to be restored even after the browser has been closed, the following example is a model for the applicability of JSON.stringify():

js
// 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()

¥Well-formed JSON.stringify()

实现 格式良好的 JSON.stringify 规范 的引擎将使用 Unicode 转义序列而不是字面意义(输出单独代理)对单独代理(从 U+D800 到 U+DFFF 的任何代码点)进行字符串化。在此更改之前,此类字符串无法以有效的 UTF-8 或 UTF-16 进行编码:

¥Engines implementing the well-formed JSON.stringify specification will stringify lone surrogates (any code point from U+D800 to U+DFFF) using Unicode escape sequences rather than literally (outputting lone surrogates). Before this change, such strings could not be encoded in valid UTF-8 or UTF-16:

js
JSON.stringify("\uD800"); // '"�"'

但通过此更改,JSON.stringify() 表示使用 JSON 转义序列的单独代理,可以用有效的 UTF-8 或 UTF-16 进行编码:

¥But with this change JSON.stringify() represents lone surrogates using JSON escape sequences that can be encoded in valid UTF-8 or UTF-16:

js
JSON.stringify("\uD800"); // '"\\ud800"'

只要你将 JSON.stringify() 的结果传递给接受任何有效 JSON 文本的 API(例如 JSON.parse()),此更改就应该向后兼容,因为它们会将单独代理的 Unicode 转义视为与单独代理本身相同。只有当你直接解释 JSON.stringify() 的结果时,你才需要仔细处理 JSON.stringify() 对这些代码点的两种可能的编码。

¥This change should be backwards-compatible as long as you pass the result of JSON.stringify() to APIs such as JSON.parse() that will accept any valid JSON text, because they will treat Unicode escapes of lone surrogates as identical to the lone surrogates themselves. Only if you are directly interpreting the result of JSON.stringify() do you need to carefully handle JSON.stringify()'s two possible encodings of these code points.

规范

Specification
ECMAScript Language Specification
# sec-json.stringify

¥Specifications

浏览器兼容性

BCD tables only load in the browser

¥Browser compatibility

也可以看看