你知道JavaScript Symbol类型怎么用吗

根据规范,对象的属性键只能是字符串类型或者 Symbol 类型。不是 Number,也不是 Boolean,只有字符串或 Symbol 这两种类型。

到目前为止,我们只见过字符串。现在我们来看看 Symbol 能给我们带来什么好处。

Symbol

"Symbol" 值表示唯一的标识符。

可以使用 Symbol() 来创建这种类型的值:

// id 是 symbol 的一个实例化对象 let id = Symbol();

创建时,我们可以给 Symbol 一个描述(也称为 Symbol 名),这在代码调试时非常有用:

// id 是描述为 "id" 的 Symbol let id = Symbol("id");

Symbol 保证是唯一的。即使我们创建了许多具有相同描述的 Symbol,它们的值也是不同。描述只是一个标签,不影响任何东西。

例如,这里有两个描述相同的 Symbol —— 它们不相等:

let id1 = Symbol("id"); let id2 = Symbol("id"); alert(id1 == id2); // false

如果你熟悉 Ruby 或者其他有 "Symbol" 的语言 —— 别被误导。JavaScript 的 Symbol 是不同的。

注意:Symbol 不会被自动转换为字符串

JavaScript 中的大多数值都支持字符串的隐式转换。例如,我们可以 alert 任何值,都可以生效。Symbol 比较特殊,它不会被自动转换。

例如,这个 alert 将会提示出错:

let id = Symbol("id"); alert(id); // 类型错误:无法将 Symbol 值转换为字符串。

这是一种防止混乱的“语言保护”,因为字符串和 Symbol 有本质上的不同,不应该意外地将它们转换成另一个。

如果我们真的想显示一个 Symbol,我们需要在它上面调用 .toString(),如下所示:

let id = Symbol("id"); alert(id.toString()); // Symbol(id),现在它有效了

或者获取 symbol.description 属性,只显示描述(description):

let id = Symbol("id"); alert(id.description); // id

“隐藏”属性

Symbol 允许我们创建对象的“隐藏”属性,代码的任何其他部分都不能意外访问或重写这些属性。

例如,如果我们使用的是属于第三方代码的 user 对象,我们想要给它们添加一些标识符。

我们可以给它们使用 Symbol 键:

let user = { // 属于另一个代码 name: "John" }; let id = Symbol("id"); user[id] = 1; alert( user[id] ); // 我们可以使用 Symbol 作为键来访问数据

在字符串 "id" 上使用 Symbol("id") 有什么好处?

因为 user 属于另一个代码,另一个代码也使用它执行一些操作,所以我们不应该在它上面加任何字段,这样很不安全。但是 Symbol 不会被意外访问到,所以第三方代码看不到它,所以使用 Symbol 也许不会有什么问题。
另外,假设另一个脚本希望在 user 中有自己的标识符,以实现自己的目的。这可能是另一个 JavaScript 库,因此脚本之间完全不了解彼此。

然后该脚本可以创建自己的 Symbol("id"),像这样:

// ... let id = Symbol("id"); user[id] = "Their id value";

我们的标识符和他们的标识符之间不会有冲突,因为 Symbol 总是不同的,即使它们有相同的名字。

……但如果我们处于同样的目的,使用字符串 "id" 而不是用 symbol,那么 就会 出现冲突:

let user = { name: "John" }; // 我们的脚本使用了 "id" 属性。 user.id = "Our id value"; // ……另一个脚本也想将 "id" 用于它的目的…… user.id = "Their id value" // 砰!无意中被另一个脚本重写了 id!

字面量中的 Symbol

如果我们要在对象字面量 {...} 中使用 Symbol,则需要使用方括号把它括起来。

就像这样:

let id = Symbol("id"); let user = { name: "John", [id]: 123 // 而不是 "id:123" };

这是因为我们需要变量 id 的值作为键,而不是字符串 "id"。

Symbol 在 for..in 中会被跳过

Symbolic 属性不参与 for..in 循环。

例如:

let id = Symbol("id"); let user = { name: "John", age: 30, [id]: 123 }; for (let key in user) alert(key); // name, age (no symbols) // 使用 Symbol 任务直接访问 alert( "Direct: " + user[id] );

Object.keys(user) 也会忽略它们。这是一般“隐藏符号属性”原则的一部分。如果另一个脚本或库遍历我们的对象,它不会意外地访问到符号属性。

相反,会同时复制字符串和 symbol 属性:

let id = Symbol("id"); let user = { [id]: 123 }; let clone = Object.assign({}, user); alert( clone[id] ); // 123

这里并不矛盾,就是这样设计的。这里的想法是当我们克隆或者合并一个 object 时,通常希望 所有 属性被复制(包括像 id 这样的 Symbol)。

其他类型的属性键被强制为字符串:

我们只能在对象中使用字符串或 symbol 作为键,其它类型会被转换为字符串。

例如,在作为属性键使用时,数字 0 变成了字符串 "0":

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/98d6efff59fa36bb2a89c1938f606ce9.html