JavaScript Symbol對象

JavaScript Symbol對象

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/infor/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>