本文已参与「新人创作礼」活动,一起开启掘金创作之路。
介绍
Symbol 是 ES6 新引入的第 6 种原始类型,用作非字符串的属性名。新标准将 Symbol 属性与对象中其他属性分别分类。
Symbol 出现以前,无论属性名由什么元素构成,全部通过一个字符串类型的名称来访问。ES6 和之后版本 Symbol 可以为属性添加非字符串名称。
使用
创建 Symbol
Symbol 类型没有字面量语法,需要通过全局的 Symbol 函数创建一个 Symbol。Symbol 函数接收一个可选参数(文本描述),增强代码可读性和便于调试 Symbol。
const firstName = Symbol("first name");
const person = {};
person[firstName] = "Zhao";
console.log("first name" in person); // false
console.log(person[firstName]); // "Zhao"
console.log(firstName); // "Symbol(first name)"
Symbol 的描述被存储在内部的 [[Description]] 属性中,只有当调用 Symbol 的 toString() 方法时才可以读取到。执行 console.log() 时隐时的调用了 firstName 的 toString() 方法。
注:不要对 Symbol 函数使用 new,它并不是一个构造器,也不会创建一个对象。
typof 也支持返回 “symbol”,可以用 typeof 来检测变量是否为 Symbol 类型。
console.log(typeof firstName) // “symbol”
Symbol 是一个函数对象,即使每次传入的参数都一样,也永远不会返回相同的值。Symbol 的主要意义是创建一个类字符串的不会与其他任何值冲突的值。 这意味着可以将 Symbol 安全地为对象添加新属性,而无须担心可能重写已有的属性。可以使用一个 Symbol 作为事件名的常量。
但是在不同的代码中如何有效的共享这些 Symbol,这就需要 Symbol 共享体系。
Symbol 共享体系
const uid = Symbol("uid");
const uid2 = Symbol("uid");
console.log(uid === uid2); // false
ES6 创建了一个全局 Symbol 注册表,我们想在不同的代码中共享 Symbol,可以使用 Symbol.for() 方法。该方法只接收一个参数,和创建 Symbol 时一样。
const uid = Symbol.for("uid");
const obj = {};
obj[uid] = "12345";
const uid2 = Symbol.for("uid");
console.log(obj[uid]); // "12345"
console.log(obj[uid2]); // "12345"
console.log(uid === uid2); // true
Symbol.for() 方法首先在全局 Symbol 注册表中搜索键为 ”uid” 的 Symbol 是否存在,如果存在就返回已有的 Symbol,否则创建一个新的 Symbol。
上面例子中,第一次调用Symbol.for("uid")会创建这个 Symbol,第二次调用会直接从 Symbol 的全局注册表中检索到这个 Symbol。
还可以使用Symbol.keyFor()在 Symbol 全局注册表中检索与 Symbol 有关的键。如下:
const uid = Symbol("uid");
const uid2 = Symbol("uid");
console.log(Symbol.keyFor(uid)); // "uid"
console.log(Symbol.keyFor(uid2)); // "uid"
well-konw Symbol 暴露内部操作
ES6 中主要通过在原型链上定义与 Symbol 相关的属性来暴露更多的语言内部逻辑。开发者可以通过多种方法修改对象的特性。
比如 Symbol.isConcatSpreadable:
const collection = {
0: "hello",
1: "world",
length: 2,
[Symbol.isConcatSpreadable]: true,
};
let messages = ["hi"].concat(collection);
console.log(messages.length);
a; // 3
console.log(messages); // ["hi","hello","world"]
Symbol.isConcatSpreadable 属性是一个布尔值,如果是 true,表示对象有 length 和数字键,所以它的数值型属性值应该被独立添加到 concat() 调用的结果中。