JSON.stringify()

Baseline 已廣泛支援

此特性已相當成熟,可在許多裝置和瀏覽器版本上使用。自 ⁨2015 年 7 月⁩以來,各瀏覽器均已提供此特性。

JSON.stringify() 靜態方法將 JavaScript 值轉換為 JSON 字串,如果指定了替換函式,則可以選擇替換值;如果指定了替換陣列,則可以選擇僅包含指定的屬性。

試一試

console.log(JSON.stringify({ x: 5, y: 6 }));
// Expected output: '{"x":5,"y":6}'

console.log(
  JSON.stringify([new Number(3), new String("false"), new Boolean(false)]),
);
// Expected output: '[3,"false",false]'

console.log(JSON.stringify({ x: [10, undefined, function () {}, Symbol("")] }));
// Expected output: '{"x":[10,null,null,null]}'

console.log(JSON.stringify(new Date(2006, 0, 2, 15, 4, 5)));
// Expected output: '"2006-01-02T15:04:05.000Z"'

語法

js
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。

異常

TypeError

在以下情況之一中丟擲

  • value 包含迴圈引用。
  • 遇到了 BigInt 值。

描述

JSON.stringify() 將值轉換為該值表示的 JSON 符號。值將按如下方式字串化:

  • BooleanNumberStringBigInt(可透過 Object() 獲取)物件在字串化過程中會根據傳統的轉換語義轉換為相應的原始值。 Symbol 物件(可透過 Object() 獲取)被視為普通物件。
  • 嘗試序列化 BigInt 值將會丟擲錯誤。但是,如果 BigInt 具有 toJSON() 方法(透過猴子補丁:BigInt.prototype.toJSON = ...),則該方法可以提供序列化結果。此約束確保使用者始終透過顯式提供的方式來確保正確的序列化(以及非常可能的,其伴隨的反序列化)行為。
  • undefinedFunctionSymbol 值不是有效的 JSON 值。如果在轉換過程中遇到任何此類值,它們將被省略(當在物件中找到時)或更改為 null(當在陣列中找到時)。當傳遞“純”值(如 JSON.stringify(() => {})JSON.stringify(undefined))時,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() 直接在該物件上呼叫,則為空字串

      所有 Temporal 物件都實現了 toJSON() 方法,該方法返回一個字串(與呼叫 toString() 相同)。因此,它們將被序列化為字串。類似地,Date 物件實現了 toJSON(),它返回與 toISOString() 相同的值。

    • 只有 可列舉的自有屬性 才會被訪問。這意味著 MapSet 等將變成 "{}"。您可以使用 replacer 引數將它們序列化為更有用的內容。

      屬性的訪問使用與 Object.keys() 相同的演算法,該演算法具有明確定義的順序,並且在不同實現之間是穩定的。例如,對同一物件使用 JSON.stringify 總是會生成相同的字串,而 JSON.parse(JSON.stringify(obj)) 將生成一個鍵順序與原始物件相同的物件(假設該物件完全是 JSON 可序列化的)。

替換引數

replacer 引數可以是函式或陣列。

作為陣列時,其元素指定要在生成的 JSON 字串中包含的物件屬性的名稱。只有字串和數字值會被考慮在內;符號鍵會被忽略。

作為函式時,它接受兩個引數:正在字串化的 keyvalue。找到鍵的物件作為 replacerthis 上下文提供。

replacer 函式也會被呼叫來處理正在字串化的初始物件,在這種情況下,key 是一個空字串("")。然後,它會為正在字串化的物件或陣列中的每個屬性呼叫。陣列索引將以字串形式作為 key 提供。當前屬性值將被 replacer 的返回值替換用於字串化。這意味著:

  • 如果您返回一個數字、字串、布林值或 null,該值將被直接序列化並用作屬性的值。(返回 BigInt 也會丟擲錯誤。)
  • 如果您返回一個 FunctionSymbolundefined,則該屬性不會包含在輸出中。
  • 如果您返回任何其他物件,該物件將被遞迴地字串化,並對每個屬性呼叫 replacer 函式。

注意: 在解析使用 replacer 函式生成的 JSON 時,您可能希望使用 reviver 引數來執行反向操作。

通常,陣列元素的索引永遠不會改變(即使元素是無效值,如函式,它也會變成 null 而不是被省略)。使用 replacer 函式可以透過返回不同的陣列來控制陣列元素的順序。

space 引數

space 引數可用於控制最終字串中的空格。

  • 如果它是一個數字,字串化的每個連續級別都將使用此數量的空格進行縮排。
  • 如果它是一個字串,連續的級別將使用此字串進行縮排。

每個縮排級別永遠不會超過 10 個字元。space 的數字值將被限制為 10,字串值將被截斷為 10 個字元。

示例

使用 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

將函式用作替換器

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 區分初始物件和具有空字串屬性的鍵(因為兩者都會得到空字串作為鍵,並可能有一個物件作為值),您需要跟蹤迭代次數(如果超出第一次迭代,則是一個真正的空字串鍵)。

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

將陣列用作替換器

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

使用 space 引數

使用一個空格縮排輸出

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

使用製表符模仿標準的漂亮列印外觀

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

toJSON() 行為

為物件定義 toJSON() 可以覆蓋其序列化行為。

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

序列化迴圈引用的問題

由於 JSON 格式不支援物件引用(儘管存在 IETF 草案),因此嘗試編碼包含迴圈引用的物件時會丟擲 TypeError

js
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() 適用性的模型。

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 規範的引擎將使用 Unicode 轉義序列來字串化獨立的代理(任何程式碼點從 U+D800 到 U+DFFF),而不是直接輸出(輸出獨立的代理)。在此更改之前,此類字串無法在有效的 UTF-8 或 UTF-16 中編碼。

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

但經過此更改後,JSON.stringify() 會使用 可以 在有效 UTF-8 或 UTF-16 中編碼的 JSON 轉義序列來表示獨立的代理。

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

此更改應該是向後相容的,只要您將 JSON.stringify() 的結果傳遞給接受任何有效 JSON 文字的 API(如 JSON.parse()),因為它們會將獨立的代理的 Unicode 轉義視為與獨立的代理本身相同。*只有*當您直接解釋 JSON.stringify() 的結果時,您才需要仔細處理這些程式碼點的 JSON.stringify() 的兩種可能編碼。

規範

規範
ECMAScript® 2026 語言規範
# sec-json.stringify

瀏覽器相容性

另見