Javascript之 Symbol 類型
根據規範,對象屬性鍵只能是string類型或symbol類型,不能是number、boolean,只有string和symbol兩種類型。
我們一般比較屬性string,現在看看symbols類型給我們帶來的優勢。 Symbols
“Symbol”值代表用給定名稱作為唯一標識。這種類型的值可以這樣建立:Symbol(name):
// id is a symbol with the name "id"let id = Symbol("id");
Symbols確保唯一,即使我們使用相同的名稱,也會產生不同的值。舉例,這裡有兩個symbol使用相同的名稱,兩種不相等:
let id1 = Symbol("id");let id2 = Symbol("id");alert(id1 == id2); // false
如果你熟悉Ruby或其他語言,也有相同類型的symbol,不要被誤導,Javascript的symbol是不同的。 隱藏屬性
symbol允許建立對象的隱藏屬性,這樣其他代碼不會偶爾訪問或覆蓋。
舉例,如果我們想儲存物件user的“identifier”,我們可以建立id的symbol:
let user = { name: “John” };
let id = Symbol(“id”);
user[id] = "ID Value";alert( user[id] ); // we can access the data using the symbol as the key
現在讓我們想像假設另外的指令碼想給user對象添加自己的“id”屬性,為其自有的目的。可能是另外其他的Javascript庫,所以完全相互不清楚彼此。
沒有問題,能夠建立自己的Symbol("id")。
代碼如下:
// ...let id = Symbol("id");user[id] = "Their id value";
這是沒有衝突的,因為symbol總是不同的,即使有相同的名稱。注意如果我們使用字串“id”代替symbol實現相同目的,那麼會有衝突:
let user = { name: "John" };// our script uses "id" propertyuser.id = "ID Value";// ...if later another script the uses "id" for its purposes...user.id = "Their id value"// boom! overwritten! it did not mean to harm the colleague, but did it!
直接量使用symbol
如果我們在一個直接量對象中使用symbol,我們需要使用方括弧:
let id = Symbol("id");let user = { name: "John", [id]: 123 // not just "id: 123"};
因為我們需要名稱為“id”的symbol變數值,不是字串“id”。 Symbol 被 for…in 忽略
symbol屬性不參與for..in迴圈,舉例:
let id = Symbol("id");let user = { name: "John", age: 30, [id]: 123};for(let key in user) alert(key); // name, age (no symbols)// the direct access by the symbol worksalert( "Direct: " + user[id] );
這時一般隱藏概念的一部分,如果其他指令碼或庫,也不期望訪問symbol屬性。
相反,Object.assign同時拷貝字元屬性和symbol屬性。
let id = Symbol("id");let user = { [id]: 123};let clone = Object.assign({}, user);alert( clone[id] ); // 123
這兩者沒有矛盾,規範就是這麼設計的,其思想是當我們複製對象或合并對象,通常希望symbol屬性也被拷貝。
其他類型的屬性鍵被強制轉換成字串:
對象中的鍵只能使用字串或symbol,其他類型強制被轉成字串。
let obj = { 0: "test" // same as "0": "test"}// both alerts access the same property (the number 0 is converted to string "0")alert( obj["0"] ); // testalert( obj[0] ); // test (same property)
全域symbol
通常,所有symbol是不同的,但有時我們想相同名詞的symbol是相同的。舉例,我們應用不同部分想訪問名詞為”id“的symbol,當然需要是相同的。
可以通過全域symbol註冊實現,我們能先建立,然後訪問他們,並且保證通過相同名詞重複訪問獲得相同的symbol。
在註冊中建立或讀取,使用文法為:symbol.for(name),舉例:
// read from the global registrylet name = Symbol.for("name"); // if the symbol did not exist, it is created// read it againlet nameAgain = Symbol.for("name");// the same symbolalert( name === nameAgain ); // true
在註冊中symbol稱為全域symbol,如果我們有應用範圍的symbol,代碼都可以訪問,可以使用全域symbol。
其他程式設計語言,想Ruby,每個名詞有單個symbol,在javascript中,我們知道,正是全域symbol。 Symbol.keyFor
對全域symbol,不僅有Symbol.for(name),根據名稱返回symbol,也有相反的調用:Symbol.keyFor(name),功能相反:根據全域symbol返回名稱。
舉例:
let sym = Symbol.for("name");let sym2 = Symbol.for("id");// get name from symbolalert( Symbol.keyFor(sym) ); // namealert( Symbol.keyFor(sym2) ); // id
Symbol.keyfor內部實現,使用全域symbol註冊,根據symbol尋找symbol名稱。
所以對非全域的symbol不其作用,如果不是全域symbol,則找不到,返回undefined。
舉例:
alert( Symbol.keyFor(Symbol.for("name")) ); // name, global symbolalert( Symbol.keyFor(Symbol("name2")) ); // undefined, non-global symbol
非全域symbol,名稱僅用於調試目的。 系統symbol
在Javascript內部存在很多系統symbol,我們能使用他們調整對象的各個方面。
這裡列出一些常用的symbol: Symbol.hasInstance Symbol.isConcatSpreadable Symbol.iterator Symbol.toPrimitive …
舉例:Symbol.toPrimitive用於對象轉基本類型時描述對象,如果你繼續深入學習Javascript,其他的你也會逐步熟悉。 總結 symbol是基本類型,實現唯一標識 通過調用symbol(name)建立symbol 我們建立一個欄位,僅為知道對應symbol的人能訪問,使用symbol很有用 symbol不會出現在for..in結果中 使用symbol(name)建立的symbol,總是不同,即使name相同。如果希望相同名稱的symbol相等,則使用全域註冊 symbol.for(name)返回給定名稱的全域symbol,多次調用返回相同symbol Javascript有系統symbol,通過Symbol.*訪問。我們能使用他們去修改一些內建行為。
技術上,symbol並不是100%隱藏,有內建方法Object.getOwnPropertySymbols(obj)可以獲得所有的symbol。
也有一個方法Reflect.ownKeys(obj)返回對象所有的鍵,包括symbol。
所以並不是真正隱藏。但大多數庫內建方法和文法結構遵循通用約定他們是隱藏的,如果某人顯示調用上述方法,可能完全理解他正在做什麼。