JavaScript Symbol物件

雲崖先生發表於2020-07-29

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>

 

相關文章