99re热这里只有精品视频,7777色鬼xxxx欧美色妇,国产成人精品一区二三区在线观看,内射爽无广熟女亚洲,精品人妻av一区二区三区

Javascript symbol 類型

2023-02-17 10:44 更新

根據規(guī)范,只有兩種原始類型可以用作對象屬性鍵:

  • 字符串類型
  • symbol 類型

否則,如果使用另一種類型,例如數字,它會被自動轉換為字符串。所以 obj[1] 與 obj["1"] 相同,而 obj[true] 與 obj["true"] 相同。

到目前為止,我們一直只使用字符串。

現在我們來看看 symbol 能給我們帶來什么。

symbol

“symbol” 值表示唯一的標識符。

可以使用 Symbol() 來創(chuàng)建這種類型的值:

let id = Symbol();

創(chuàng)建時,我們可以給 symbol 一個描述(也稱為 symbol 名),這在代碼調試時非常有用:

// id 是描述為 "id" 的 symbol
let id = Symbol("id");

symbol 保證是唯一的。即使我們創(chuàng)建了許多具有相同描述的 symbol,它們的值也是不同。描述只是一個標簽,不影響任何東西。

例如,這里有兩個描述相同的 symbol —— 它們不相等:

let id1 = Symbol("id");
let id2 = Symbol("id");

alert(id1 == id2); // false

如果你熟悉 Ruby 或者其他有 “symbol” 的語言 —— 別被誤導。JavaScript 的 symbol 是不同的。

所以,總而言之,symbol 是帶有可選描述的“原始唯一值”。讓我們看看我們可以在哪里使用它們。

symbol 不會被自動轉換為字符串

JavaScript 中的大多數值都支持字符串的隱式轉換。例如,我們可以 alert 任何值,都可以生效。symbol 比較特殊,它不會被自動轉換。

例如,這個 alert 將會提示出錯:

let id = Symbol("id");
alert(id); // 類型錯誤:無法將 symbol 值轉換為字符串。

這是一種防止混亂的“語言保護”,因為字符串和 symbol 有本質上的不同,不應該意外地將它們轉換成另一個。

如果我們真的想顯示一個 symbol,我們需要在它上面調用 .toString(),如下所示:

let id = Symbol("id");
alert(id.toString()); // Symbol(id),現在它有效了

或者獲取 symbol.description 屬性,只顯示描述(description):

let id = Symbol("id");
alert(id.description); // id

“隱藏”屬性

symbol 允許我們創(chuàng)建對象的“隱藏”屬性,代碼的任何其他部分都不能意外訪問或重寫這些屬性。

例如,如果我們使用的是屬于第三方代碼的 user 對象,我們想要給它們添加一些標識符。

我們可以給它們使用 symbol 鍵:

let user = { // 屬于另一個代碼
  name: "John"
};

let id = Symbol("id");

user[id] = 1;

alert( user[id] ); // 我們可以使用 symbol 作為鍵來訪問數據

使用 Symbol("id") 作為鍵,比起用字符串 "id" 來有什么好處呢?

由于 user 對象屬于另一個代碼庫,所以向它們添加字段是不安全的,因為我們可能會影響代碼庫中的其他預定義行為。但 symbol 屬性不會被意外訪問到。第三方代碼不會知道新定義的 symbol,因此將 symbol 添加到 user 對象是安全的。

另外,假設另一個腳本希望在 user 中有自己的標識符,以實現自己的目的。

那么,該腳本可以創(chuàng)建自己的 Symbol("id"),像這樣:

// ...
let id = Symbol("id");

user[id] = "Their id value";

我們的標識符和它們的標識符之間不會有沖突,因為 symbol 總是不同的,即使它們有相同的名字。

……但如果我們處于同樣的目的,使用字符串 "id" 而不是用 symbol,那么 就會 出現沖突:

let user = { name: "John" };

// 我們的腳本使用了 "id" 屬性。
user.id = "Our id value";

// ……另一個腳本也想將 "id" 用于它的目的……

user.id = "Their id value"
// 砰!無意中被另一個腳本重寫了 id!

對象字面量中的 symbol

如果我們要在對象字面量 {...} 中使用 symbol,則需要使用方括號把它括起來。

就像這樣:

let id = Symbol("id");

let user = {
  name: "John",
  [id]: 123 // 而不是 "id":123
};

這是因為我們需要變量 id 的值作為鍵,而不是字符串 “id”。

symbol 在 for…in 中會被跳過

symbol 屬性不參與 for..in 循環(huán)。

例如:

let id = Symbol("id");
let user = {
  name: "John",
  age: 30,
  [id]: 123
};

for (let key in user) alert(key); // name, age(沒有 symbol)

// 使用 symbol 任務直接訪問
alert("Direct: " + user[id]); // Direct: 123

Object.keys(user) 也會忽略它們。這是一般“隱藏符號屬性”原則的一部分。如果另一個腳本或庫遍歷我們的對象,它不會意外地訪問到符號屬性。

相反,Object.assign 會同時復制字符串和 symbol 屬性:

let id = Symbol("id");
let user = {
  [id]: 123
};

let clone = Object.assign({}, user);

alert( clone[id] ); // 123

這里并不矛盾,就是這樣設計的。這里的想法是當我們克隆或者合并一個 object 時,通常希望 所有 屬性被復制(包括像 id 這樣的 symbol)。

全局 symbol

正如我們所看到的,通常所有的 symbol 都是不同的,即使它們有相同的名字。但有時我們想要名字相同的 symbol 具有相同的實體。例如,應用程序的不同部分想要訪問的 symbol "id" 指的是完全相同的屬性。

為了實現這一點,這里有一個 全局 symbol 注冊表。我們可以在其中創(chuàng)建 symbol 并在稍后訪問它們,它可以確保每次訪問相同名字的 symbol 時,返回的都是相同的 symbol。

要從注冊表中讀?。ú淮嬖趧t創(chuàng)建)symbol,請使用 Symbol.for(key)。

該調用會檢查全局注冊表,如果有一個描述為 key 的 symbol,則返回該 symbol,否則將創(chuàng)建一個新 symbol(Symbol(key)),并通過給定的 key 將其存儲在注冊表中。

例如:

// 從全局注冊表中讀取
let id = Symbol.for("id"); // 如果該 symbol 不存在,則創(chuàng)建它

// 再次讀取(可能是在代碼中的另一個位置)
let idAgain = Symbol.for("id");

// 相同的 symbol
alert( id === idAgain ); // true

注冊表內的 symbol 被稱為 全局 symbol。如果我們想要一個應用程序范圍內的 symbol,可以在代碼中隨處訪問 —— 這就是它們的用途。

這聽起來像 Ruby

在一些編程語言中,例如 Ruby,每個名字都有一個 symbol。

正如我們所看到的,在 JavaScript 中,全局 symbol 也是這樣的。

Symbol.keyFor

我們已經看到,對于全局 symbol,Symbol.for(key) 按名字返回一個 symbol。相反,通過全局 symbol 返回一個名字,我們可以使用 Symbol.keyFor(sym)

例如:

// 通過 name 獲取 symbol
let sym = Symbol.for("name");
let sym2 = Symbol.for("id");

// 通過 symbol 獲取 name
alert( Symbol.keyFor(sym) ); // name
alert( Symbol.keyFor(sym2) ); // id

Symbol.keyFor 內部使用全局 symbol 注冊表來查找 symbol 的鍵。所以它不適用于非全局 symbol。如果 symbol 不是全局的,它將無法找到它并返回 undefined。

也就是說,所有 symbol 都具有 description 屬性。

例如:

let globalSymbol = Symbol.for("name");
let localSymbol = Symbol("name");

alert( Symbol.keyFor(globalSymbol) ); // name,全局 symbol
alert( Symbol.keyFor(localSymbol) ); // undefined,非全局

alert( localSymbol.description ); // name

系統(tǒng) symbol

JavaScript 內部有很多“系統(tǒng)” symbol,我們可以使用它們來微調對象的各個方面。

它們都被列在了 眾所周知的 symbol 表的規(guī)范中:

  • ?Symbol.hasInstance?
  • ?Symbol.isConcatSpreadable?
  • ?Symbol.iterator?
  • ?Symbol.toPrimitive?
  • ……等等。

例如,Symbol.toPrimitive 允許我們將對象描述為原始值轉換。我們很快就會看到它的使用。

當我們研究相應的語言特征時,我們對其他的 symbol 也會慢慢熟悉起來。

總結

symbol 是唯一標識符的基本類型

symbol 是使用帶有可選描述(name)的 Symbol() 調用創(chuàng)建的。

symbol 總是不同的值,即使它們有相同的名字。如果我們希望同名的 symbol 相等,那么我們應該使用全局注冊表:Symbol.for(key) 返回(如果需要的話則創(chuàng)建)一個以 key 作為名字的全局 symbol。使用 Symbol.for 多次調用 key 相同的 symbol 時,返回的就是同一個 symbol。

symbol 有兩個主要的使用場景:

  1. “隱藏” 對象屬性。
  2. 如果我們想要向“屬于”另一個腳本或者庫的對象添加一個屬性,我們可以創(chuàng)建一個 symbol 并使用它作為屬性的鍵。symbol 屬性不會出現在 for..in 中,因此它不會意外地被與其他屬性一起處理。并且,它不會被直接訪問,因為另一個腳本沒有我們的 symbol。因此,該屬性將受到保護,防止被意外使用或重寫。

    因此我們可以使用 symbol 屬性“秘密地”將一些東西隱藏到我們需要的對象中,但其他地方看不到它。

  3. JavaScript 使用了許多系統(tǒng) symbol,這些 symbol 可以作為 ?Symbol.*? 訪問。我們可以使用它們來改變一些內建行為。例如,在本教程的后面部分,我們將使用 ?Symbol.iterator? 來進行 迭代 操作,使用 ?Symbol.toPrimitive? 來設置 對象原始值的轉換 等等。

從技術上說,symbol 不是 100% 隱藏的。有一個內建方法 Object.getOwnPropertySymbols(obj) 允許我們獲取所有的 symbol。還有一個名為 Reflect.ownKeys(obj) 的方法可以返回一個對象的 所有 鍵,包括 symbol。但大多數庫、內建方法和語法結構都沒有使用這些方法。


以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號