符号
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
在下一课中,我们将讨论用于创建符号的全局资源。