根據規範,物件屬性鍵只能是string類型或symbol類型,不能是number、boolean,只有string和symbol兩種類型。
我們一般比較屬性string,現在來看看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" property user.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,我們需要使用方括號:
let id = Symbol("id"); let user = { name: "John", [id]: 123 // not just "id: 123" };
因為我們需要名稱為「id」的symbol變數值,不是字串「id」。
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 works alert( "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"] ); // test alert( obj[0] ); // test (same property)
通常,所有symbol是不同的,但有時我們想相同名詞的symbol是相同的。舉例,我們應用不同部分想訪問名詞為”id“的symbol,當然需要是相同的。
可以透過全域symbol註冊實現,我們能先創建,然後訪問他們,並且保證透過相同名詞重複訪問獲得相同的symbol。
在註冊中建立或讀取,使用語法為:symbol.for(name)
,舉例:
// read from the global registry let name = Symbol.for("name"); // if the symbol did not exist, it is created // read it again let nameAgain = Symbol.for("name"); // the same symbol alert( name === nameAgain ); // true
在註冊中symbol稱為全域symbol,如果我們有應用範圍的symbol,代碼都可以訪問,可以使用全域symbol。
其他程式語言,想Ruby,每個名詞有單一symbol,在javascript中,我們知道,正是全局symbol。
對全域symbol,不僅有Symbol.for(name)
,根據名稱傳回symbol,也有相反的呼叫:Symbol.keyFor (name)
,功能相反:根據全域symbol傳回名稱。
範例:
let sym = Symbol.for("name"); let sym2 = Symbol.for("id"); // get name from symbol alert( Symbol.keyFor(sym) ); // name alert( Symbol.keyFor(sym2) ); // id
Symbol.keyfor內部實現,使用全域symbol註冊,根據symbol尋找symbol名稱。
所以對非全局的symbol不其作用,如果不是全域symbol,則找不到,回傳undefined。
範例:
alert( Symbol.keyFor(Symbol.for("name")) ); // name, global symbol alert( Symbol.keyFor(Symbol("name2")) ); // undefined, non-global symbol
非全域symbol,名稱僅用於偵錯目的。
在Javascript內部存在著許多系統symbol,我們可以使用他們調整物件的各個面向。
這裡列出一些常用的symbol:
Symbol.hasInstance
Symbol.isConcatSpreadable
#Symbol.iterator
Symbol.toPrimitive
…
Symbol.toPrimitive用於對象轉基本型別時描述對象,如果你繼續深入學習Javascript,其他的你也會逐步熟悉。
symbol(name)創建symbol
技術上,symbol並不是100%隱藏,有內建方法Object.getOwnPropertySymbols(obj)
可以得到所有的symbol。
也有一個方法Reflect.ownKeys(obj)
傳回物件所有的鍵,包括symbol。
所以並不是真正隱藏。但大多數庫內建方法和語法結構遵循通用約定他們是隱藏的,如果某人顯示調用上述方法,可能完全理解他正在做什麼。
以上是Javascript中關於Symbol類型的具體詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!