根据规范,对象的属性键只能是字符串类型或者 Symbol 类型,到目前为止,我们只见过字符串。现在我们来看看 Symbol 能给我们带来什么好处。
1、symbol值表示唯一的标识符,即使创建了许多具有相同描述的symbol,它们的值也不相同,描述只是一个标签,不影响任何东西
let id1 = Symbol("id")
let id2 = Symbol("id")
alert(id1==id2) // false
2、symbol不会被自动转换为字符串
let id = Symbol("id")
alert(id) // Cannot convert a Symbol value to a string
这是一种防止混乱的“语言保护”,因为字符串和 Symbol 有本质上的不同,不应该意外地将它们转换成另一个。
let id = Symbol("id")
alert(id.toString()) // Symbol(id)
或者获取 symbol.description 属性,只显示描述(description):
let id = Symbol("id")
alert(id.description) // id
3、隐藏属性
Symbol 允许我们创建对象的“隐藏”属性,代码的任何其他部分都不能意外访问或重写这些属性。
let user = { // 属于另一个代码
name: "John",
id: '7777777777'
};
let id = Symbol("id");
user[id] = 1;
console.log(user) // {name: "John", id: '7777777777', Symbol(id): 1}
console.log(user[id]) // 1
console.log(user.id) // 7777777777
使用 Symbol("id") 作为键,比起用字符串 "id" 来有什么好处呢?
因为 user 对象属于其他的代码,那些代码也会使用这个对象,所以我们不应该在它上面直接添加任何字段,这样很不安全。但是你添加的 Symbol 属性不会被意外访问到,第三方代码根本不会看到它,所以使用 Symbol 基本上不会有问题。
另外,假设另一个脚本希望在 user 中有自己的标识符,以实现自己的目的。这可能是另一个 JavaScript 库,因此脚本之间完全不了解彼此。
然后该脚本可以创建自己的 Symbol("id")
我们的标识符和它们的标识符之间不会有冲突,因为 Symbol 总是不同的,即使它们有相同的名字。
……但如果我们处于同样的目的,使用字符串 "id" 而不是用 symbol,那么 就会 出现冲突
4、对象字面量中的Symbol,如果我们要在对象字面量 {...} 中使用 Symbol,则需要使用方括号把它括起来。
let id = Symbol("id")
let user = { name: "John",[id]:123}
5、Symbol 属性不参与 for..in 循环
Object.keys(user) 也会忽略它们。这是一般“隐藏符号属性”原则的一部分。如果另一个脚本或库遍历我们的对象,它不会意外地访问到符号属性。
相反,Object.assign 会同时复制字符串和 symbol 属性:
let id = Symbol("id")
let user = { [id]: 123}
let clone = Object.assign({}, user)
alert( clone[id] ); // 123
6、全局Symbol
// 从全局注册表中读取
let id = Symbol.for("id")// 如果该 Symbol 不存在,则创建它
// 再次读取(可能是在代码中的另一个位置)
let idAgain = Symbol.for("id") // 相同的
Symbolalert( id === idAgain ); // true
注册表内的 Symbol 被称为 全局 Symbol。如果我们想要一个应用程序范围内的 Symbol,可以在代码中随处访问 —— 这就是它们的用途。
7、Symbol.keyFor
对于全局 Symbol,不仅有 Symbol.for(key) 按名字返回一个 Symbol,还有一个反向调用:Symbol.keyFor(sym),它的作用完全反过来:通过全局 Symbol 返回一个名字。
// 通过 name 获取 Symbol
let sym = Symbol.for("name")
let sym2 = Symbol.for("id") // 通过 Symbol 获取 name
alert( Symbol.keyFor(sym) ); // name
alert( Symbol.keyFor(sym2) ); // id
Symbol.keyFor 内部使用全局 Symbol 注册表来查找 Symbol 的键。所以它不适用于非全局 Symbol。如果 Symbol 不是全局的,它将无法找到它并返回 undefined。
也就是说,任何 Symbol 都具有 description 属性。
let globalSymbol = Symbol.for("name");
let localSymbol = Symbol("name");
alert( Symbol.keyFor(globalSymbol) ); // name,全局 Symbol
alert( Symbol.keyFor(localSymbol) ); // undefined,非全局
alert( localSymbol.description ); // name
8、系统Symbol
JavaScript 使用了许多系统 Symbol,这些 Symbol 可以作为 Symbol.* 访问。我们可以使用它们来改变一些内置行为。