## 20.符號
> 原文: [http://exploringjs.com/impatient-js/ch_symbols.html](http://exploringjs.com/impatient-js/ch_symbols.html)
>
> 貢獻者:[飛龍](https://github.com/wizardforcel)
符號是通過工廠函數`Symbol()`創建的原始值:
```js
const mySymbol = Symbol('mySymbol');
```
該參數是可選的,并提供了一個描述,主要用于調試。
一方面,符號就像對象一樣,`Symbol()`創建的每個值都是唯一的,不按值進行比較:
```js
> Symbol() === Symbol()
false
```
另一方面,它們也表現得像原始值 - 它們必須通過`typeof`進行分類,它們可以是對象中的屬性鍵:
```js
const sym = Symbol();
assert.equal(typeof sym, 'symbol');
const obj = {
[sym]: 123,
};
```
### 20.1。符號用例
符號的主要用例是:
* 用于值的常量
* 唯一的屬性鍵
#### 20.1.1。符號:用于值的常量
假設您要創建表示紅色,橙色,黃色,綠色,藍色和紫色的常量。這樣做的一個簡單方法是使用字符串:
```js
const COLOR_BLUE = 'Blue';
```
一方面,記錄該常量會產生有用的輸出。另一方面,存在將無關值誤認為顏色的風險,因為具有相同內容的兩個字符串被認為是相等的:
```js
const MOOD_BLUE = 'Blue';
assert.equal(COLOR_BLUE, MOOD_BLUE);
```
我們可以通過符號來解決這個問題:
```js
const COLOR_BLUE = Symbol('Blue');
const MOOD_BLUE = 'Blue';
assert.notEqual(COLOR_BLUE, MOOD_BLUE);
```
讓我們使用符號值常量來實現一個函數:
```js
const COLOR_RED = Symbol('Red');
const COLOR_ORANGE = Symbol('Orange');
const COLOR_YELLOW = Symbol('Yellow');
const COLOR_GREEN = Symbol('Green');
const COLOR_BLUE = Symbol('Blue');
const COLOR_VIOLET = Symbol('Violet');
function getComplement(color) {
switch (color) {
case COLOR_RED:
return COLOR_GREEN;
case COLOR_ORANGE:
return COLOR_BLUE;
case COLOR_YELLOW:
return COLOR_VIOLET;
case COLOR_GREEN:
return COLOR_RED;
case COLOR_BLUE:
return COLOR_ORANGE;
case COLOR_VIOLET:
return COLOR_YELLOW;
default:
throw new Exception('Unknown color: '+color);
}
}
assert.equal(getComplement(COLOR_YELLOW), COLOR_VIOLET);
```
值得注意的是,如果用`'Blue'`調用它,該函數會拋出異常:
```js
assert.throws(() => getComplement('Blue'));
```
#### 20.1.2。符號:唯一的屬性鍵
對象中屬性(字段)的鍵在兩個級別使用:
* 程序在基礎級別上運行。這一級別上的鍵反映了程序所解決的問題。
* 庫和 ECMAScript 在元級別上運行。這一級別的鍵由服務使用,他們運行在基礎級別的數據和代碼上。 一個這樣的鍵是`toString`。
以下代碼演示了不同之處:
```js
const point = {
x: 7,
y: 4,
toString() {
return `(${this.x}, ${this.y})`;
},
};
assert.equal(String(point), '(7, 4)');
```
屬性`.x`和`.y`存在于基礎級別。它們是由`point`編碼的點的坐標,反映了問題領域。方法`.toString()`是元級別屬性。它告訴 JavaScript 如何對此對象進行字符串化。
元級別和基礎級別絕不能發生沖突,這在編程語言生命后期引入新機制時變得更加困難。
符號可用作屬性鍵并解決此問題:每個符號都是唯一的,不會與任何字符串或任何其他符號沖突。
作為一個例子,我們假設我們正在編寫一個庫,如果它們實現了一個特殊的方法,它會以不同的方式對待它們。這就是定義這種方法的屬性鍵并為對象實現它的方法如下所示:
```js
const specialMethod = Symbol('specialMethod');
const obj = {
[specialMethod](x) {
return x + x;
}
};
assert.equal(obj[specialMethod]('abc'), 'abcabc');
```
在[對象](ch_single-objects.html#computed-property-keys)的章節中更詳細地解釋了這種語法。
### 20.2。眾所周知的符號
在 ECMAScript 中起到特殊作用的符號稱為*公認符號*。例子包括:
* `Symbol.iterator`:使對象*可迭代*。它是返回迭代器的方法鍵。迭代在[它自己的章節](ch_sync-iteration.html)中進行了解釋。
* `Symbol.hasInstance`:自定義`instanceof`的工作方式。如果對象使用該鍵實現方法,則可以在該運算符的右側使用它。例如:
```js
class PrimitiveNull {
static [Symbol.hasInstance](x) {
return x === null;
}
}
assert.equal(null instanceof PrimitiveNull, true);
Symbol.toStringTag: influences the default .toString() method.
```
* `Symbol.toStringTag`:影響默認`.toString()`方法。
注意:覆蓋`.toString()`通常更好。
```js
> String({})
'[object Object]'
> String({ [Symbol.toStringTag]: 'is no money' })
'[object is no money]'
```
 **練習:公認符號**
* `Symbol.toStringTag`:`exercises/symbols/to_string_tag_test.js`
* `Symbol.hasInstance`:`exercises/symbols/has_instance_test.js`
### 20.3。轉換符號
如果我們將符號`sym`轉換為另一種原始類型會發生什么?表 17 就是答案。
Table 17: 將符號轉換為其他原始類型的結果。
| 轉換為 | 顯式轉換 | 強制轉換(隱式轉換) |
| --- | --- | --- |
| 布爾值 | `Boolean(sym) →`正常 | `!sym →`正常 |
| 數值 | `Number(sym) → TypeError` | `sym*2 → TypeError` |
| 字符串 | `String(sym) →`正常 | `''+sym → TypeError` |
| | `sym.toString() →`正常 | `` `${sym}` → TypeError `` |
符號的一個關鍵缺陷是,將它們轉換為其他內容時拋出異常的頻率。背后的想法是什么?首先,轉換為數字永遠不會有意義,應該加以警告。其次,將符號轉換為字符串對于診斷輸出確實很有用。但是,警告意外地將符號轉換為字符串屬性鍵也是有意義的:
```js
const obj = {};
const sym = Symbol();
assert.throws(
() => { obj['__'+sym+'__'] = true },
{ message: 'Cannot convert a Symbol value to a string' });
```
缺點是異常使符號處理更復雜。通過加法運算符組裝字符串時必須顯式轉換符號:
```js
> const mySymbol = Symbol('mySymbol');
> 'Symbol I used: ' + mySymbol
TypeError: Cannot convert a Symbol value to a string
> 'Symbol I used: ' + String(mySymbol)
'Symbol I used: Symbol(mySymbol)'
```
### 20.4。深入閱讀
* 有關符號(跨域符號,所有公認符號等)的深入信息,請參閱[“探索 ES6”](http://exploringjs.com/es6/ch_symbols.html)
 **測驗**
參見[測驗應用程序](ch_quizzes-exercises.html#quizzes)。
- I.背景
- 1.關于本書(ES2019 版)
- 2.常見問題:本書
- 3. JavaScript 的歷史和演變
- 4.常見問題:JavaScript
- II.第一步
- 5.概覽
- 6.語法
- 7.在控制臺上打印信息(console.*)
- 8.斷言 API
- 9.測驗和練習入門
- III.變量和值
- 10.變量和賦值
- 11.值
- 12.運算符
- IV.原始值
- 13.非值undefined和null
- 14.布爾值
- 15.數字
- 16. Math
- 17. Unicode - 簡要介紹(高級)
- 18.字符串
- 19.使用模板字面值和標記模板
- 20.符號
- V.控制流和數據流
- 21.控制流語句
- 22.異常處理
- 23.可調用值
- VI.模塊化
- 24.模塊
- 25.單個對象
- 26.原型鏈和類
- 七.集合
- 27.同步迭代
- 28.數組(Array)
- 29.類型化數組:處理二進制數據(高級)
- 30.映射(Map)
- 31. WeakMaps(WeakMap)
- 32.集(Set)
- 33. WeakSets(WeakSet)
- 34.解構
- 35.同步生成器(高級)
- 八.異步
- 36. JavaScript 中的異步編程
- 37.異步編程的 Promise
- 38.異步函數
- IX.更多標準庫
- 39.正則表達式(RegExp)
- 40.日期(Date)
- 41.創建和解析 JSON(JSON)
- 42.其余章節在哪里?