符號

Baseline 廣泛可用 *

此特性已相當成熟,可在許多裝置和瀏覽器版本上使用。自 2015 年 9 月以來,該特性已在各大瀏覽器中可用。

* 此特性的某些部分可能存在不同級別的支援。

Symbol 是一個內建物件,其建構函式返回一個 symbol 原始值——也稱為 Symbol 值或簡稱 Symbol——它保證是獨一無二的。Symbol 通常用於向物件新增唯一的屬性鍵,這些鍵不會與其他程式碼可能新增到物件的鍵衝突,並且對其他程式碼通常用於訪問物件的任何機制都是隱藏的。這使得一種弱形式的封裝,或者一種弱形式的資訊隱藏成為可能。

每次 Symbol() 呼叫都保證返回一個唯一的 Symbol。每次 Symbol.for("key") 呼叫都會針對給定 "key" 值始終返回相同的 Symbol。當呼叫 Symbol.for("key") 時,如果在全域性 Symbol 登錄檔中找到具有給定鍵的 Symbol,則返回該 Symbol。否則,將建立一個新的 Symbol,以給定鍵新增到全域性 Symbol 登錄檔,然後返回。

描述

要建立一個新的原始 Symbol,你可以使用 Symbol(),並可選地傳入一個字串作為其描述。

js
const sym1 = Symbol();
const sym2 = Symbol("foo");
const sym3 = Symbol("foo");

上述程式碼建立了三個新的 Symbol。注意,Symbol("foo") 不會將字串 "foo" 強制轉換為 Symbol。它每次都會建立一個新的 Symbol。

js
Symbol("foo") === Symbol("foo"); // false

使用 new 運算子的以下語法將丟擲 TypeError

js
const sym = new Symbol(); // TypeError

這可以防止開發者建立顯式的 Symbol 包裝物件而不是新的 Symbol 值,這可能會讓人感到驚訝,因為通常可以為原始資料型別建立顯式的包裝物件(例如,new Booleannew Stringnew Number)。

如果你確實想建立一個 Symbol 包裝物件,你可以使用 Object() 函式。

js
const sym = Symbol("foo");
typeof sym; // "symbol"
const symObj = Object(sym);
typeof symObj; // "object"

因為 Symbol 是唯一具有引用標識(即,你不能兩次建立相同的 Symbol)的原始資料型別,所以它們在某些方面表現得像物件。例如,它們是可以被垃圾回收的,因此可以儲存在 WeakMapWeakSetWeakRefFinalizationRegistry 物件中。

全域性 Symbol 登錄檔中的共享 Symbol

上述使用 Symbol() 函式的語法將建立一個 Symbol,其值在程式生命週期內保持唯一。要建立跨檔案甚至跨領域(每個領域都有自己的全域性作用域)可用的 Symbol,請使用 Symbol.for()Symbol.keyFor() 方法從全域性 Symbol 登錄檔中設定和檢索 Symbol。

請注意,“全域性 Symbol 登錄檔”只是一個虛擬概念,可能不對應於 JavaScript 引擎中的任何內部資料結構——即使存在這樣的登錄檔,其內容也無法透過 JavaScript 程式碼訪問,除了透過 for()keyFor() 方法。

方法 Symbol.for(tokenString) 接受一個字串鍵並從登錄檔中返回一個 Symbol 值,而 Symbol.keyFor(symbolValue) 接受一個 Symbol 值並返回與之對應的字串鍵。兩者互為逆運算,因此以下結果為 true

js
Symbol.keyFor(Symbol.for("tokenString")) === "tokenString"; // true

由於註冊 Symbol 可以隨意建立,它們幾乎完全像它們包裝的字串一樣。因此,它們不保證是唯一的,並且不能被垃圾回收。因此,註冊 Symbol 不允許在 WeakMapWeakSetWeakRefFinalizationRegistry 物件中使用。

知名 Symbol

Symbol 建構函式的所有靜態屬性本身都是 Symbol,它們的值在不同領域是常量。它們被稱為 知名 Symbol,其目的是充當某些內建 JavaScript 操作的“協議”,允許使用者自定義語言的行為。例如,如果一個建構函式有一個以 Symbol.hasInstance 為名稱的方法,這個方法將使用 instanceof 運算子來編碼其行為。

在知名 Symbol 之前,JavaScript 使用普通屬性來實現某些內建操作。例如,JSON.stringify 函式會嘗試呼叫每個物件的 toJSON() 方法,String 函式會呼叫物件的 toString()valueOf() 方法。然而,隨著語言中添加了更多的操作,將每個操作指定為“魔術屬性”可能會破壞向後相容性,並使語言行為更難理解。知名 Symbol 允許自定義對普通程式碼“不可見”,這些程式碼通常只讀取字串屬性。

注意:規範過去使用 @@<symbol-name> 符號來表示知名 Symbol。例如,Symbol.hasInstance 寫成 @@hasInstance,而 Array.prototype[Symbol.iterator]() 方法則被稱為 Array.prototype[@@iterator]()。這種符號在規範中不再使用,但你可能仍然在舊文件或討論中看到它。

知名 Symbol 沒有垃圾回收的概念,因為它們以固定集合存在,並且在程式的整個生命週期內都是唯一的,類似於 Array.prototype 等固有物件,因此它們也允許在 WeakMapWeakSetWeakRefFinalizationRegistry 物件中使用。

在物件上查詢 Symbol 屬性

方法 Object.getOwnPropertySymbols() 返回一個 Symbol 陣列,允許你在給定物件上查詢 Symbol 屬性。請注意,每個物件在初始化時都沒有自己的 Symbol 屬性,因此除非你已在該物件上設定了 Symbol 屬性,否則此陣列將為空。

建構函式

Symbol()

返回 Symbol 型別的原始值。使用 new 呼叫時會丟擲錯誤。

靜態屬性

靜態屬性都是知名 Symbol。在這些 Symbol 的描述中,我們將使用“Symbol.hasInstance 是一個方法,用於確定……”之類的語言,但請記住,這指的是一個物件的方法以這個 Symbol 作為方法名稱的語義(因為知名 Symbol 充當“協議”),而不是描述 Symbol 值本身。

Symbol.asyncDispose

當物件超出作用域時,非同步釋放物件資源的方法。由 await using 宣告使用。

Symbol.asyncIterator

返回物件預設 AsyncIterator 的方法。由 for await...of 使用。

Symbol.dispose

當物件超出作用域時,釋放物件資源的方法。由 using 宣告使用。

Symbol.hasInstance

一個確定建構函式物件是否將其識別為其例項的方法。由 instanceof 使用。

Symbol.isConcatSpreadable

一個布林值,指示物件是否應被展平為其陣列元素。由 Array.prototype.concat() 使用。

Symbol.iterator

返回物件預設迭代器的方法。由 for...of 使用。

Symbol.match

與字串匹配的方法,也用於確定物件是否可用作正則表示式。由 String.prototype.match() 使用。

Symbol.matchAll

返回一個迭代器的方法,該迭代器生成正則表示式與字串的匹配項。由 String.prototype.matchAll() 使用。

Symbol.replace

替換字串中匹配的子字串的方法。由 String.prototype.replace() 使用。

Symbol.search

返回字串中與正則表示式匹配的索引的方法。由 String.prototype.search() 使用。

Symbol.species

用於建立派生物件的建構函式。

Symbol.split

在與正則表示式匹配的索引處分割字串的方法。由 String.prototype.split() 使用。

Symbol.toPrimitive

將物件轉換為原始值的方法。

Symbol.toStringTag

用於物件預設描述的字串值。由 Object.prototype.toString() 使用。

Symbol.unscopables

一個物件值,其自身和繼承的屬性名稱被排除在關聯物件的 with 環境繫結之外。

靜態方法

Symbol.for()

在全域性 Symbol 登錄檔中搜索具有給定 key 的現有註冊 Symbol,如果找到則返回。否則,將建立一個新的 Symbol 並以 key 註冊。

Symbol.keyFor()

從全域性 Symbol 登錄檔中檢索給定 Symbol 的共享 Symbol 鍵。

例項屬性

這些屬性定義在 Symbol.prototype 上,並由所有 Symbol 例項共享。

Symbol.prototype.constructor

建立例項物件的建構函式。對於 Symbol 例項,初始值是 Symbol 建構函式。

Symbol.prototype.description

一個只讀字串,包含 Symbol 的描述。

Symbol.prototype[Symbol.toStringTag]

[Symbol.toStringTag] 屬性的初始值是字串 "Symbol"。此屬性在 Object.prototype.toString() 中使用。然而,由於 Symbol 也有其自己的 toString() 方法,除非你以 Symbol 作為 thisArg 呼叫 Object.prototype.toString.call(),否則此屬性不會被使用。

例項方法

Symbol.prototype.toString()

返回一個包含 Symbol 描述的字串。覆蓋 Object.prototype.toString() 方法。

Symbol.prototype.valueOf()

返回 Symbol。覆蓋 Object.prototype.valueOf() 方法。

Symbol.prototype[Symbol.toPrimitive]()

返回 Symbol。

示例

將 typeof 運算子與 Symbol 一起使用

typeof 運算子可以幫助你識別 Symbol。

js
typeof Symbol() === "symbol";
typeof Symbol("foo") === "symbol";
typeof Symbol.iterator === "symbol";

Symbol 型別轉換

在使用 Symbol 型別轉換時需要注意的一些事項。

  • 當嘗試將 Symbol 轉換為數字時,會丟擲 TypeError(例如,+symsym | 0)。
  • 使用鬆散相等時,Object(sym) == sym 返回 true
  • Symbol("foo") + "bar" 會丟擲 TypeError(無法將 Symbol 轉換為字串)。這可以防止你悄悄地從 Symbol 建立新的字串屬性名,例如。
  • “更安全”的 String(sym) 轉換 與對 Symbol 呼叫 Symbol.prototype.toString() 類似,但請注意 new String(sym) 會丟擲錯誤。

Symbol 和 for...in 迭代

Symbol 在 for...in 迭代中不可列舉。此外,Object.getOwnPropertyNames() 不會返回 Symbol 物件屬性,但是,你可以使用 Object.getOwnPropertySymbols() 來獲取這些屬性。

js
const obj = {};

obj[Symbol("a")] = "a";
obj[Symbol.for("b")] = "b";
obj["c"] = "c";
obj.d = "d";

for (const i in obj) {
  console.log(i);
}
// "c" "d"

Symbol 和 JSON.stringify()

使用 JSON.stringify() 時,Symbol 鍵的屬性將被完全忽略。

js
JSON.stringify({ [Symbol("foo")]: "foo" });
// '{}'

欲瞭解更多詳情,請參見 JSON.stringify()

Symbol 包裝物件作為屬性鍵

當 Symbol 包裝物件用作屬性鍵時,此物件將被強制轉換為其包裝的 Symbol。

js
const sym = Symbol("foo");
const obj = { [sym]: 1 };
obj[sym]; // 1
obj[Object(sym)]; // still 1

規範

規範
ECMAScript® 2026 語言規範
# sec-symbol-objects

瀏覽器相容性

另見