|
| 1 | +## 7.5 把 Symbol 转换成其他原始类型 |
| 2 | + |
| 3 | +下面的表格展示了 Symbol 在显示地或者隐式地转换成其他原始类型的时候会发生什么: |
| 4 | + |
| 5 | +| 转换成 | 显示转换 | 隐式转换 | |
| 6 | +| -- | -- | -- | |
| 7 | +| boolean | Boolean(sym) → OK | !sym → OK | |
| 8 | +| number | Number(sym) → TypeError | sym*2 → TypeError | |
| 9 | +| string | String(sym) → OK | ''+sym → TypeError | |
| 10 | +| | sym.toString() → OK | \`${sym}` → TypeError | |
| 11 | + |
| 12 | +### 7.5.1 当心:隐式转换成字符串 |
| 13 | + |
| 14 | +禁止隐式转换成字符串会很容易导致下面的错误: |
| 15 | + |
| 16 | +```js |
| 17 | +const sym = Symbol(); |
| 18 | + |
| 19 | +console.log('A symbol: '+sym); // TypeError |
| 20 | +console.log(`A symbol: ${sym}`); // TypeError |
| 21 | +``` |
| 22 | + |
| 23 | +要修复这些问题,需要显示转换成字符串: |
| 24 | + |
| 25 | +```js |
| 26 | +console.log('A symbol: '+String(sym)); // OK |
| 27 | +console.log(`A symbol: ${String(sym)}`); // OK |
| 28 | +``` |
| 29 | + |
| 30 | +### 7.5.2 理解隐式转换规则 |
| 31 | + |
| 32 | +通常会禁止 Symbol 的隐式转换。本节讲解了为什么会这样。 |
| 33 | + |
| 34 | +#### 7.5.2.1 允许真值检查 |
| 35 | + |
| 36 | +隐式转换成布尔值是始终可以的,主要用于在 if 语句中检查真值情况以及其他一些场景: |
| 37 | + |
| 38 | +```js |
| 39 | +if (value) { ··· } |
| 40 | + |
| 41 | +param = param || 0; |
| 42 | +``` |
| 43 | + |
| 44 | +#### 7.5.2.2 意外地将 Symbol 用作属性键 |
| 45 | + |
| 46 | +Symbol 是一种特殊的属性键,这就是为什么需要避免意外地将其转换成字符串(字符串是另一种类型的属性键)。在使用一些操作符来计算属性名的时候,这种意外错误就可能发生: |
| 47 | + |
| 48 | +```js |
| 49 | +myObject['__' + value] |
| 50 | +``` |
| 51 | + |
| 52 | +这就是为啥 `value` 是 Symbol 的时候会抛出 `TypeError` 错误。 |
| 53 | + |
| 54 | +#### 7.5.2.3 意外地将 Symbol 用作数组索引 |
| 55 | + |
| 56 | +你肯定也不期望意外地将 Symbol 用作数组索引。下面的代码展示了当 `value` 是 Symbol 的时候,这种错误就会发生: |
| 57 | + |
| 58 | +```js |
| 59 | +myArray[1 + value] |
| 60 | +``` |
| 61 | + |
| 62 | +这就是为什么在这种场景中,该操作会抛出错误。 |
| 63 | + |
| 64 | +### 7.5.3 规范中的显示转换和隐式转换 |
| 65 | + |
| 66 | +#### 7.5.3.1 转换成布尔类型 |
| 67 | + |
| 68 | +可以使用 [Boolean()](http://www.ecma-international.org/ecma-262/6.0/#sec-boolean-constructor-boolean-value) 显示地将 Symbol 转换成布尔类型。对于 Symbol 类型,会始终返回 `true` : |
| 69 | + |
| 70 | +``` |
| 71 | +> const sym = Symbol('hello'); |
| 72 | +> Boolean(sym) |
| 73 | +true |
| 74 | +``` |
| 75 | + |
| 76 | +`Boolean()` 通过内部的 [ToBoolean()](http://www.ecma-international.org/ecma-262/6.0/#sec-toboolean) 操作计算出最终结果。对于 Symbol 和其他真值,该操作返回 `true` 。 |
| 77 | + |
| 78 | +隐式转换同样使用 `ToBoolean()` : |
| 79 | + |
| 80 | +``` |
| 81 | +> !sym |
| 82 | +false |
| 83 | +``` |
| 84 | + |
| 85 | +#### 7.5.3.2 转换成数值类型 |
| 86 | + |
| 87 | +使用 [Number()](http://www.ecma-international.org/ecma-262/6.0/#sec-number-constructor-number-value) 将 Symbol 显示地转换成数值类型: |
| 88 | + |
| 89 | +``` |
| 90 | +> const sym = Symbol('hello'); |
| 91 | +> Number(sym) |
| 92 | +TypeError: can't convert symbol to number |
| 93 | +``` |
| 94 | + |
| 95 | +`Number()` 通过内部的 [ToNumber()](http://www.ecma-international.org/ecma-262/6.0/#sec-tonumber) 操作计算出最终结果,对于 Symbol ,该操作会抛出 `TypeError` 。 |
| 96 | + |
| 97 | +隐式转换也会使用 [ToNumber()] : |
| 98 | + |
| 99 | +``` |
| 100 | +> +sym |
| 101 | +TypeError: can't convert symbol to number |
| 102 | +``` |
| 103 | + |
| 104 | +#### 7.5.3.3 转换成字符串 |
| 105 | + |
| 106 | +使用 [String()](http://www.ecma-international.org/ecma-262/6.0/#sec-string-constructor-string-value) 将 Symbol 转换成字符串: |
| 107 | + |
| 108 | +``` |
| 109 | +> const sym = Symbol('hello'); |
| 110 | +> String(sym) |
| 111 | +'Symbol(hello)' |
| 112 | +``` |
| 113 | + |
| 114 | +如果 `String()` 的参数是 Symbol ,那么该方法会自己将其转换成字符串,然后返回用创建 Symbol 的时候传入的描述字符串(使用 `Symbol()` 包裹起来)。如果没有提供描述字符串,就会使用空字符串: |
| 115 | + |
| 116 | +``` |
| 117 | +> String(Symbol()) |
| 118 | +'Symbol()' |
| 119 | +``` |
| 120 | + |
| 121 | +`toString()` 方法返回和 `String()` 相同的结果,但是都不会调用对方,它们都会调用内部的操作 [SymbolDescriptiveString()](http://www.ecma-international.org/ecma-262/6.0/#sec-symboldescriptivestring) 。 |
| 122 | + |
| 123 | +``` |
| 124 | +> Symbol('hello').toString() |
| 125 | +'Symbol(hello)' |
| 126 | +``` |
| 127 | + |
| 128 | +隐式转换是通过内部的 [ToString()](http://www.ecma-international.org/ecma-262/6.0/#sec-tostring) 操作来处理的,对于 Symbol ,会抛出 `TypeError` 。`Number.parseInt()` 方法会隐式地将参数转换成字符串: |
| 129 | + |
| 130 | +``` |
| 131 | +> Number.parseInt(Symbol()) |
| 132 | +TypeError: can't convert symbol to string |
| 133 | +``` |
| 134 | + |
| 135 | +#### 7.5.3.4 不允许:使用双目加号操作符(+)转换 |
| 136 | + |
| 137 | +[加号操作符](http://www.ecma-international.org/ecma-262/6.0/#sec-addition-operator-plus)的处理过程是这样的: |
| 138 | + |
| 139 | +- 将两边的操作数转换成原始类型。 |
| 140 | +- 如果某一个操作数是字符串,那么另一个操作数会被隐式转换成字符串(使用 `ToString()`),然后将它们串联起来,返回结果。 |
| 141 | +- 否则,将两个操作数都转换成数值类型,然后相加,返回结果。 |
| 142 | + |
| 143 | +隐式转换成字符串和数值都会抛出异常,这意味着不能(直接)将加号运算符用于 Symbol 。 |
| 144 | + |
| 145 | +``` |
| 146 | +> '' + Symbol() |
| 147 | +TypeError: can't convert symbol to string |
| 148 | +> 1 + Symbol() |
| 149 | +TypeError: can't convert symbol to number |
| 150 | +``` |
0 commit comments