Logo
Section 1 / 6

符号

symbol - ES6 中的一种新原始类型及其使用场景

导入

符号

symbol - ES6 中的一种新原始类型及其使用场景

ES6 引入了一种新的 JavaScript 原始类型:符号(Symbols)。全局的 Symbol() 函数用于创建 JavaScript 符号。每次调用 Symbol() 函数时,都会返回一个全新的唯一符号。

1let symbol1 = Symbol(); 2let symbol2 = Symbol(); 3 4console.log(symbol1 === symbol2); 5//> false

符号没有字面量值。你只需要知道,每个符号都被视为一个唯一的值。换句话说,没有任何两个符号是相等的。

符号是 JavaScript 中的一种新 类型。

1console.log(typeof symbol1); 2//> "symbol"

符号非常有用,因为它们可以作为对象的唯一键。

1let myObject = { 2 publicProperty: 'Value of myObject["publicProperty"]' 3}; 4 5myObject[symbol1] = 'Value of myObject[symbol1]'; 6myObject[symbol2] = 'value of myObject[symbol2]'; 7 8console.log(myObject); 9//> Object 10//> publicProperty: "Value of myObject[\"publicProperty\"]" 11//> Symbol(): "Value of myObject[symbol1]" 12//> Symbol(): "value of myObject[symbol2]" 13//> __proto__: Object 14 15console.log(myObject[symbol1]); 16//> Value of myObject[symbol1]

在 Chrome 开发者工具中打印 myObject 时,可以看到两个符号属性都存储在对象中。"Symbol()" 是符号调用 toString() 方法后的返回值,这个值表示控制台中存在符号键。只要拥有正确的符号,就可以获取对应的值。

在某些环境(如 Node.js 和 Microsoft Edge 控制台)中,符号键不会被显示。如果你看不到这两个符号属性,问题并不在于你的复制或输入能力。

符号属性在 Node.js 的控制台(代码编辑器底部)中不会显示,但在 Google Chrome 开发者工具(代码编辑器下方)中会显示。

1console.dir(myObject, {showHidden: true})

具有符号键的属性不会出现在对象的 JSON 表示中。即使 for...in 循环或 Object.keys 也无法枚举它们:

1JSON.stringify(myObject) 2//> "{"publicProperty":"Value of myObject[\"publicProperty\"]"}" 3 4for (var prop in myObject) { 5 console.log(prop, myObject[prop]); 6} 7//> publicProperty Value of myObject["publicProperty"] 8 9console.log(Object.keys(myObject)); 10//> ["publicProperty"]

尽管上述情况下符号键的属性不会出现,但这些属性从严格意义上来说并不是完全私有的。Object.getOwnPropertySymbols 提供了一种获取对象符号键的方法:

1console.log(Object.getOwnPropertySymbols(myObject)); 2//> [Symbol(), Symbol()] 3 4console.log(myObject[Object.getOwnPropertySymbols(myObject)[0]]); 5//> "Value of myObject[symbol1]"

如果你选择使用符号键来表示私有变量,请确保不要使用 Object.getOwnPropertySymbols 来访问本应私有的属性。在这种情况下,Object.getOwnPropertySymbols 的唯一用途是测试和调试。

只要遵守上述规则,从开发代码的角度来看,你的对象键就是私有的。然而,在实际应用中,请意识到其他人仍然可能访问到你的私有数据。

尽管符号键不会被 for...in 或 Object.keys 枚举,但它们仍会出现在对象的浅拷贝中:

1clonedObject = Object.assign({}, myObject); 2 3console.log(clonedObject); 4//> Object 5//> publicProperty: "Value of myObject[\"publicProperty\"]" 6//> Symbol(): "Value of myObject[symbol1]" 7//> Symbol(): "value of myObject[symbol2]" 8//> __proto__: Object

为符号正确命名非常重要,这有助于表明其用途。如果需要额外的语义提示,还可以为符号附加描述信息。符号的描述会出现在符号的字符串表示中。

1let leftNode = Symbol('Binary tree node'); 2let rightNode = Symbol('Binary tree node'); 3 4console.log(leftNode); 5//> Symbol(Binary tree node)

始终为符号提供描述,并确保描述具有唯一性。如果你用符号来访问私有属性,应将它们的描述视为变量名对待。

即使两个符号传入相同的描述,它们的值仍然不同。仅知道描述并不能让你创建出相同的符号。

1let leftNode = Symbol('Binary tree node'); 2let rightNode = Symbol('Binary tree node'); 3console.log(leftNode === rightNode); 4//> false

在下一课中,我们将讨论用于创建符号的全局资源。

ES6中的尾调用优化及其他函数特性

全局符号注册表