5-JS-对象-Symbol类型

Symbol 类型

根据规范,对象的属性键只能是字符串类型或者 Symbol 类型。不是 Number,也不是 Boolean,只有字符串或 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

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

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

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

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

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

如果要获取 symbol.description 属性,只显示描述:

let id = Symbol("id");
alert(id.description); // id
隐藏属性

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

比如:

给它们使用 Symbol 键:

let user = { // 属于另一个代码
  name: "John"
};

let id = Symbol("id");

user[id] = 1;

alert( user[id] ); // 我们可以使用 Symbol 作为键来访问数据

使用 Symbol("id") 作为键,比起用字符串 "id" 来有什么好处呢?

因为 user 对象属于其他的代码,那些代码也会使用这个对象,所以我们不应该在它上面直接添加任何字段,这样很不安全。但是你添加的 Symbol 属性不会被意外访问到,第三方代码根本不会看到它,所以使用 Symbol 基本上不会有问题。

同时,脚本可以创建自己的 Symbol(“id”)

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

user[id] = "Their id value";

如果我们有相同的目的,使用字符串 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
};
Symbol 在 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) 也会忽略它们。这是一般“隐藏符号属性”原则的一部分。如果另一个脚本或库遍历我们的对象,它不会意外地访问到符号属性。

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

let id = Symbol("id");
let user = {
  [id]: 123
};

let clone = Object.assign({}, user);

alert( clone[id] ); // 123
全局 Symbol

如我们所看到的,通常所有的 Symbol 都是不同的,即使它们有相同的名字。但有时我们想要名字相同的 Symbol 具有相同的实体。例如,应用程序的不同部分想要访问的 Symbol "id" 指的是完全相同的属性。

为了实现这一点,这里有一个 全局 Symbol 注册表。我们可以在其中创建 Symbol 并在稍后访问它们,它可以确保每次访问相同名字的 Symbol 时,返回的都是相同的 Symbol。

要从注册表中读取(不存在则创建)Symbol,请使用 Symbol.for(key)

该调用会检查全局注册表,如果有一个描述为 key 的 Symbol,则返回该 Symbol,否则将创建一个新 Symbol(Symbol(key)),并通过给定的 key 将其存储在注册表中。

例如:

// 从全局注册表中读取
let id = Symbol.for("id"); // 如果该 Symbol 不存在,则创建它

// 再次读取(可能是在代码中的另一个位置)
let idAgain = Symbol.for("id");

// 相同的 Symbol
alert( id === idAgain ); // true
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
系统 Symbol

avaScript 内部有很多“系统” Symbol,我们可以使用它们来微调对象的各个方面。

它们都被列在了 众所周知的 Symbol 表的规范中:

  • Symbol.hasInstance
  • Symbol.isConcatSpreadable
  • Symbol.iterator
  • Symbol.toPrimitive
  • ……等等。

例如,Symbol.toPrimitive 允许我们将对象描述为原始值转换。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值