Symbol
Symbol
物件是es6
中新引進的一種資料型別,它的作用非常簡單,就是用於防止屬性名衝突而產生。
Symbol
的最大特點就是值是具有唯一性,這代表使用Symbol
型別的值能做獨一無二的一些事情。
此外,Symbol
沒有建構函式,這使得我們不能new
它,直接使用即可。
宣告Symbol
使用Symbol()
宣告一個獨一無二的值。
<script> "use strict"; let Sym1 = Symbol(); // 獨一無二的值 let Sym2 = Symbol(); console.log(typeof Sym1); // symbol console.log(Sym1 == Sym2); // false </script>
描述資訊
為Symbol
的值在宣告時新增一段描述資訊。
使用description
屬性可檢視描述資訊。
注意:即使兩個
Symbol
的描述資訊是一樣的也不會有什麼問題,因為它僅僅是描述資訊而已。
<script> "use strict"; let Sym1 = Symbol("這是Sym1的描述資訊"); // 獨一無二的值 let Sym2 = Symbol("這是Sym2的描述資訊"); console.log(Sym1.description); // 這是Sym1的描述資訊 console.log(Sym2.description); // 這是Sym2的描述資訊 </script>
Symbol.for
使用Symbol()
來建立值不會進行記錄,所以無論值看起來是否一樣都不會引用同一份記憶體地址。
而使用Symbol.for()
來建立值則會進行記錄,下次再建立相同的值時會直接引用記錄的記憶體地址。
<script> "use strict"; let Sym1 = Symbol("測試"); // 獨一無二的值 let Sym2 = Symbol("測試"); console.log(Sym1 == Sym2); // false </script>
<script> "use strict"; let Sym1 = Symbol.for("測試"); // 將這個值的記憶體地址記錄,下次再建立時直接引用記憶體地址 let Sym2 = Symbol.for("測試"); console.log(Sym1 == Sym2); // true </script>
Symbol.keyFor
用於返回由Symbol.for()
建立的值的描述資訊。
如果值沒有描述資訊則返回undefined
。
當然,我們也可以使用description
屬性來獲取描述資訊,二者皆可。
<script> "use strict"; let Sym1 = Symbol.for("測試"); // 將這個值的記憶體地址記錄,下次再建立時直接引用記憶體地址 let Sym2 = Symbol.for(); console.log(Symbol.keyFor(Sym1)); // 測試 console.log(Symbol.keyFor(Sym2)); // undefined console.log(Sym1.description); // 測試 console.log(Sym2.description); // undefined </script>
實際應用
物件屬性
Js
中的物件(鍵)如果直接宣告就會變成String
型別,這在某些程度上可能會引起物件屬性衝突問題。
物件的鍵最好是唯一的,Symbol
型別的值無疑是最好的選擇。
當我們給想物件的鍵設定為Symbol
型別的值的時候需要注意2點問題。
Symbol
宣告和訪問使用[]
(變數)形式操作不能使用
.
語法因為.
語法是操作字串屬性的
<script> "use strict"; let username = Symbol(); // 將這個值的記憶體地址記錄,下次再建立時直接引用記憶體地址 let age = Symbol(); let dic = { // 宣告時加上 [] 否則會變成String型別 --> "username" [username]:"雲崖", [age]:18, }; // 不能使用 . 語法獲取值(屬性) console.log(dic[username]); // 雲崖 console.log(dic[age]); // 18 </script>
物件遍歷
Symbol
型別值不能被 for/in
、for/of
遍歷操作找到。
以下示例可以看出,找不到兩個Symbol
型別的鍵。
<script> "use strict"; let username = Symbol(); // 將這個值的記憶體地址記錄,下次再建立時直接引用記憶體地址 let age = Symbol(); let dic = { // 宣告時加上 [] 否則會變成String型別 --> "username" [username]: "雲崖", [age]: 18, "gender": "男", }; for (let i in dic) { console.log(i); // gender } // for/of 只能遍歷一個迭代物件,不能直接遍歷物件。所以我們使用Object.keys(dic)將dic轉換為一個迭代物件。 for (let i of Object.keys(dic)) { console.log(i); // gender } </script>
可以使用 Object.getOwnPropertySymbols
獲取所有Symbol
屬性(鍵)。
注意,這是僅僅獲取Symbol
的屬性(鍵)。
<script> "use strict"; let username = Symbol(); // 將這個值的記憶體地址記錄,下次再建立時直接引用記憶體地址 let age = Symbol(); let dic = { // 宣告時加上 [] 否則會變成String型別 --> "username" [username]: "雲崖", [age]: 18, "gender": "男", }; for (let i in Object.getOwnPropertySymbols(dic)) { console.log(i); // 0 1 } // for/of 只能遍歷一個迭代物件,不能直接遍歷物件。所以我們使用Object.keys(dic)將dic轉換為一個迭代物件。 for (let i of Object.getOwnPropertySymbols(dic)) { console.log(i); // (2) Symbol() } </script>
也可以使用 Reflect.ownKeys(obj)
獲取所有屬性(鍵)包括Symbol
型別的屬性。
<script> "use strict"; let username = Symbol(); // 將這個值的記憶體地址記錄,下次再建立時直接引用記憶體地址 let age = Symbol(); let dic = { // 宣告時加上 [] 否則會變成String型別 --> "username" [username]: "雲崖", [age]: 18, "gender": "男", }; for (let i in Reflect.ownKeys(dic)) { console.log(i); // 0 1 2 } console.log("*".repeat(20)); // for/of 只能遍歷一個迭代物件,不能直接遍歷物件。所以我們使用Object.keys(dic)將dic轉換為一個迭代物件。 for (let i of Reflect.ownKeys(dic)) { console.log(i); // gender (2) Symbol() } </script>
私有屬性
我們可以使用Symbol
不能被for/in
以及for/of
訪問的特性,為類製作私有屬性以及提供訪問介面。
<script> "use strict"; const sex = Symbol("性別"); class User { constructor(name, age, gender) { this[sex] = gender; // 存入類物件中 this.name = name; this.age = age; } getMsg() { // 我們希望通過提供的API介面來讓使用者調出gender屬性 return `姓名:${this.name},年齡:${this.age},性別:${this[sex]}` } } let u1 = new User("雲崖", 18, "男"); console.log(u1.getMsg()); // 只能通過介面來拿到性別 姓名:雲崖,年齡:18,性別:男 // 如果迴圈不管是for/in還是for/of都是拿不到性別的 for (const i in u1) { console.log(i); // name // age } </script>