[toc]
### 普通符號
符號的基本概念:
- 符號是ES6中新增的一種數據類型。
- 它通過使用Symbol(符號描述)來創建。符號描述為定義符號時針對該符號的描述信息,僅用于方便識別創建該符號的用途。
- 符號設計的初衷,是為了給對象設置私有屬性
- 對象的私有屬性是指該屬性只能在對象內部使用,外部是無法調用的這些屬性。
符號的特點:
- 符號沒有字面量。
- 使用typeof得到的類型為symbol。
- **每次調用Symbol得到的符號永遠不相等,無論符號名是否相同**
- 符號可以做為對象的屬性名存在,這種屬性稱之為符號屬性。
```js
const syb1 = Symbol('定義一個符號'); //創建了一個符號類型的變量syb1
const obj = {
a: 1,
b: 2,
[syb1]: 'abc'
//把syb1做為obj的一個符號屬性,定義時需用[]包裹符號變量名,屬性的內容可以隨便定義。
//函數內部調用符號屬性也需要用[符號變量名]來調用。
}
```
- 開發者可以通過一些設計,讓這些屬性無法通過常規方式被外界訪問。
>通過創建立即執行函數,在函數內創建符號屬性。可以在常規的情況下實現私有化屬性。
```js
//例如:游戲角色每次攻擊傷害值的取值過程
const hero = (function () {
const getRandom = Symbol();
return {
attack: 30,
hp: 300,
defence: 10,
gongji() { //攻擊
//傷害:攻擊力*隨機數(0.8~1.1)
const dmg = this.attack * this[getRandom](0.8, 1.1);
console.log(dmg);
},
[getRandom](min, max) { //根據最小值和最大值產生一個隨機數
return Math.random() * (max - min) + min;
}
}
})()
//調用該方法會產生一個getRandom符號屬性,因為是立即執行函數。函數執行完畢即銷毀了,所以在外部無法調用getRandom屬性。
//定義一個類的時候也可以使用同樣的方法,返回一個類表達式。
```
- 符號屬性是不能被枚舉的,因此在for-in循環中無法讀取到符號屬性,Object.key方法也無法讀取到符號屬性。
- Object.getOwnPropertyNames 盡管可以得到所有無法枚舉的屬性,但是仍然無法讀取到符號屬性
- ES6 新增 Object.getOwnPropertySymbols 方法,可以讀取符號
- 符號無法被隱式轉換,因此不能被用于數學運算、字符串拼接或其他隱式轉換的場景,但符號可以顯式的轉換為字符串,通過 String 構造函數進行轉換即可,console.log 之所以可以輸出符號,是它在內部進行了顯式轉換
>該章節重點撐握符號類型的基本特性,會使用符號屬性創建一個私有屬性。
### 共享符號
當我們在某個對象或類中創建了一個符號類型的私有屬性,但又想在其它文件的代碼中調用該符號屬性。ES6中提供了共享符號的方法。
```js
//Symbol.for("符號名/符號描述"),用來創建一個共享符號
const sy1 = Symbol.for('abc')
const sy2 = Symbol.for('abc')
sy1 === sy2 //返回的結果為true,說明雖然是兩個變量,但其指向同一個符號,可以在不同的位置使用。
```
共享符號可以從外部直接調用
```js
const obj = {
a:1,
b:2,
[Symbol.for('cc')]: 'ccc'
}
console.log(obj[Symbol.for('cc')]); //返回的結果為ccc,即可以用這種方法在外部調用共享符號屬性。
```
### 知名(公共、具名)符號
- 知名符號是一些具有特殊含義的共享符號,通過Symbol的靜態屬性得到。
- ES6延續了ES5的思想:減少魔法(指不清楚實現原理的一些方法),暴露內部實現。因此,ES6使用知名符號暴露了某些場景的內部實現。
1. Symbol.hasInstance
該符號用于定義構造函數的靜態成員,它將影響 instanceof 的判定
```js
obj instanceof A
//該符號等效于
A[Symbol.hasInstance](obj) // Function.prototype[Symbol.hasInstance]
```
2. [擴展] Symbol.isConcatSpreadable
該知名符號會影響數組的 concat 方法,只有true和false兩個參數。
當設置為true時,concat方法會將拼接的數組分割成每一個獨立的值,追加到對應的數組中。
當設置為false時,concat方法會將拼接的數組直接追加到對應的數組中。
```js
let arr = [0, 1]
let arr1 = [2, 3]
arr1[Symbol.isConcatSpreadable] = true; //拼接進行分割
let newarr = arr.concat(arr1); //newarr的值為[0,1,2,3]
arr1[Symbol.isConcatSpreadable] = false; //拼接不進行分割
let newarr = arr.concat(arr1); //newarr的值為[0,1,[2,3]]
```
3. [擴展] Symbol.toPrimitive
該知名符號會影響類型轉換的結果
```js
const obj = {
a: 1,
b: 2
}
//正常情況下obj在進行基本運算時會啟動隱式類型轉換,如obj+123,結果為:[object object]123,obj最終會被使用toString方法轉為字符串。
obj[Symbol.toPrimitive] = function(type){
return 2
}
//當使用obj的Symbol.toPrimitive知名符號,可以定義一個函數。該函數的返回值就是隱式類型轉換后的值。
const res = obj+123; //例如使用了上面的知名符號后,obj隱式類型轉換返回的結果變成2了,所以res的結果為125
//該知名符號定義的函數會返回一個type參數,該參數用來表示當前是在會哪種方式進行轉換,返回值有default、number、String。
```
4. [擴展] Symbol.toStringTag
該知名符號會影響 Object.prototype.toString 的返回值。
如果把該符號寫入一個類或對象中,指定一個名稱,那么該名稱會替換toString方法返回的第二位的內容
例如:一個對象toString后返回[object Object],說明這是一個對象。若在該對象中加入[Symbol.toStringTag] = 'Student'。那么再次執行toString后會返回[object Student]。