🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## 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]' ``` ![](https://img.kancloud.cn/3e/d5/3ed5755d562179ae6c199264f5e21157.svg) **练习:公认符号** * `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) ![](https://img.kancloud.cn/ff/a8/ffa8e16628cad59b09c786b836722faa.svg) **测验** 参见[测验应用程序](ch_quizzes-exercises.html#quizzes)。