Symbol 范例

Symbol 范例

依据范例,对象的属性键只能是 String 范例或许 Symbol 范例。不是 Number,也不是 Boolean,只要 String 或 Symbol 这两种范例。

到目前为止,我们只见过 String。如今我们来看看 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 异乎寻常。

JavaScript 中的大多数值都支撑 string 的隐式转换。比方,我们能够 `alert` 任何值,这会起作用。Symbol 是迥殊的,它没法自动转换。

比方,这个 `alert` 将会显现毛病:

let id = Symbol(“id”);
!
alert(id); // 范例毛病:没法将 Symbol 值转换为 String。
/!


假如我们真的想显现一个 Symbol,我们须要在它上面挪用 `.toString()`,以下所示:

let id = Symbol(“id”);
!
alert(id.toString()); // Symbol(id),如今它起作用了
/!


这是一种防备杂沓的“言语庇护”,由于 String 和 Symbol 有本质上的差别,而且不应当偶然将它们互相转化。

“隐蔽”属性

Symbol 许可我们建立对象的“隐蔽”属性,代码的任何其他部份都不能偶然接见或重写这些属性。

比方,假如我们想存储 object user 的“标识符”,我们能够运用 Symbol 作为它的键:

let user = { name: "John" };
let id = Symbol("id");

user[id] = "ID Value";
alert( user[id] ); // 我们能够运用 Symbol 作为键来接见数据。

在 string "id" 上运用 Symbol("id") 有什么优点?

我们用更深切一点的示例来申明这一点。

假定另一个剧本愿望 user 中有它本身的 “id” 属性能够操纵。这多是另一个 JavaScript 库,所以这些剧本完整不知道对方是谁。

然后该剧本能够建立本身的 Symbol("id"),以下所示:

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

user[id] = "Their id value";

不会争执,由于 Symbol 老是差别的,纵然它们有雷同的称号。

如今请注意,假如我们运用 String "id" 而不是用 symbol,那末就会涌现争执:

let user = { name: "John" };

//我们的剧本运用 "id" 属性。
user.id = "ID Value";

// ...假如以后另一个剧本为其目标运用 "id"...

user.id = "Their id value"
// 砰!无意中重写了 id!他不是故意伤害同事的,而是如许做了!

字面量中的 Symbol

假如我们要在 object 字面量中运用 Symbol,则须要方括号。

就像如许:

let id = Symbol("id");

let user = {
  name: "John",
*!*
  [id]: 123 // 不仅仅是 "id:123"
*/!*
};

这是由于我们须要变量 id 的值作为键,而不是 String “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] );

这是平常“隐蔽”观点的一部份。假如另一个剧本或库在我们的对象上轮回,它不会接见一个 Symbol 范例的属性。

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

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

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

alert( clone[id] ); // 123

这里并不矛盾,就是如许设想的。我们的主意是当我们克隆一个 object 或兼并 object 时,一般愿望一切属性被复制(包含像 id 如许的 Symbol)。

我们只能在对象中运用 string 或 symbol 作为键,别的范例转换为 String。

比方,在作为属性键运用时,数字 `0`变成了字符串 `"0"`:

let obj = {
0: “test” // same as “0”: “test”
};

//两个 alert 都接见雷同的属性(Number 0 被转换为 String “0”)
alert( obj[“0”] ); // test
alert( obj[0] ); // test (雷同属性)

全局 symbol

正如我们所看到的,一般一切的 Symbol 都是差别的,纵然它们有雷同的名字。但偶然我们想要同一个名字的 Symbol 是雷同的实体。

比方,我们愿望在应用程序的差别部份接见雷同的 Symbol "id" 属性。

为此,存在一个全局 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 称为全局 Symbol。假如我们想要一个应用程序范围内的 Symbol,能够在代码中到处接见 —— 这就是它们的用处。

在一些编程言语中,比方 Ruby,每一个称号都有一个 symbol。

在 JavaScript 中,我们应当用全局 symbol。

Symbol.keyFor

关于全局 symbol,Symbol.for(key) 不仅按称号返回一个 symbol,而且另有一个反向挪用:Symbol.keyFor(sym),反过来:经由过程全局 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

比方:

alert( Symbol.keyFor(Symbol.for("name")) ); // name, 全局 Symbol

alert( Symbol.keyFor(Symbol("name2")) ); // undefined, 参数不是一个全局 Symbol

体系 Symbol

JavaScript 内部存在许多“体系” Symbol,我们能够运用它们来微调对象的各个方面。

它们列在熟习的 Symbol 表的范例中:

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

比方,Symbol.toPrimitive 许可我们将对象形貌为原始值转换,我们很快就会看到它的运用。

当我们研讨响应的言语特性时,其他 Symbol 也会变得熟习起来。

总结

Symbol 是唯一标识符的基础范例

Symbol 运用 Symbol() 建立的,挪用带有一个可选的形貌。

Symbol 老是差别的值,纵然它们有雷同的称号。假如我们愿望同名 Symbol 相称,那末我们应当运用全局注册表:Symbol.for(key) 返回(假如须要的话建立)一个以 key 作为称号的全局 Symbol。Symbol.for 的屡次挪用完整返回雷同的 Symbol。

Symbol 有两个重要的运用场景:

  1. “隐蔽” 对象属性。假如须要将属性添加到 “属于” 另一个剧本或库的对象中,则能够建立 Symbol 并将其用作属性键。Symbol 属性不出如今 for..in中,因而不会无意列出。别的,它不会被直接接见,由于另一个剧本没有我们的标记,所以它不会不小心干涉干与它的操纵。

    因而我们能够运用 Symbol 属性“秘密地”将一些东西隐蔽到我们须要的对象中,但其他人不会以对象属性的情势看到它。

  2. JavaScript 运用了许多体系 Symbol,这些 Symbol 能够作为 Symbol.* 接见。我们能够运用它们来转变一些内置行动。比方,在本教程的背面部份,我们将运用 Symbol.iterator迭代Symbol.toPrimitive 来设置对象原始值的转换等等。

从技术上说,Symbol 不是 100% 隐蔽的。有一个内置要领 Object.getOwnPropertySymbols(obj) 许可我们猎取一切的 Symbol。另有一个名为 Reflect.ownKeys(obj) 返回一切键,包含 Symbol。所以它们不是真正的隐蔽。然则大多数库、内置要领和语法结构都遵照一个配合的协定。而明白挪用上述要领的人能够很清晰他在做什么。

    原文作者:邢走在云端
    原文地址: https://segmentfault.com/a/1190000019118322
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞