w3ctech

了解 ES6 中的Symbol

Symbol的辨识方法

Symbol是原始值,且ECMAScript 6同时扩展了typeof操作符,支持返回"Symbol",所以可以用typeof来检测变量是否为Symbol类型。

let symbol = Symbol("test symbol");
console.log(typeof symbol);         // "symbol"

通过其他间接方式也可以检测变量是否为Symbol类型,但是typeof操作符是最准确也是你最应首选的检测方式。

Symbol的使用方法

所有使用可计算属性名的地方,都可以使用Symbol。前面我们看到的都是在括号中使用Symbol,事实上,Symbol也可以用于可计算对象字面量属性名、Object.defineProperty()方法和Object.defineProperties()方法的调用过程中。

let firstName = Symbol("first name");


// 使用一个可计算对象字面量属性
let person = {
    [firstName]: "Nicholas"
};

// 将属性设置为只读
Object.defineProperty(person, firstName, { writable: false });

let lastName = Symbol("last name");

Object.defineProperties(person, {
    [lastName]: {
        value: "Zakas",
        writable: false
    }
});

console.log(person[firstName]);     // "Nicholas"
console.log(person[lastName]);      // "Zakas"

在此示例中,首先通过可计算对象字面量属性语法为person对象创建了一个Symbol属性firstName。后面一行代码将这个属性设置为只读。随后,通过Object.defineProperties()方法创建一个只读的Symbol属性lastName,此处再次使用了对象字面量属性,但却是作为Object.defineProperties()方法的第二个参数使用。 尽管在所有使用可计算属性名的地方,都可以使用Symbol来代替,但是为了在不同代码片段间有效地共享这些Symbol,需要建立一个体系。

Symbol共享体系

有时我们可能希望在不同的代码中共享同一个Symbol,例如,在你的应用中有两种不同的对象类型,但是你希望它们使用同一个Symbol属性来表示一个独特的标识符。一般而言,在很大的代码库中或跨文件追踪Symbol非常困难而且容易出错,出于这些原因,ECMAScript 6提供了一个可以随时访问的全局Symbol注册表。

如果想创建一个可共享的Symbol,要使用Symbol.for()方法。它只接受一个参数,也就是即将创建的Symbol的字符串标识符,这个参数同样也被用作Symbol的描述,就像这样:

let uid = Symbol.for("uid");
let object = {};

object[uid] = "12345";

console.log(object[uid]);       // "12345"
console.log(uid);               // "Symbol(uid)"

Symbol.for()方法首先在全局Symbol注册表中搜索键为"uid"的Symbol是否存在,如果存在,直接返回已有的Symbol;否则,创建一个新的Symbol,并使用这个键在Symbol全局注册表中注册,随即返回新创建的Symbol。

后续如果再传入同样的键调用Symbol.for()会返回相同的Symbol,像这样:

let uid = Symbol.for("uid");
let object = {
    [uid]: "12345"
};

console.log(object[uid]);       // "12345"
console.log(uid);               // "Symbol(uid)"

let uid2 = Symbol.for("uid");

console.log(uid === uid2);      // true
console.log(object[uid2]);      // "12345"
console.log(uid2);              // "Symbol(uid)"

在这个示例中,uid和uid2包含相同的Symbol并且可以互换使用。第一次调用Symbol.for()方法创建这个Symbol,第二次调用可以直接从Symbol的全局注册表中检索到这个Symbol。

还有一个与Symbol共享有关的特性:可以使用Symbol.keyFor()方法在Symbol全局注册表中检索与Symbol有关的键。举个例子:

let uid = Symbol.for("uid");
console.log(Symbol.keyFor(uid));    // "uid"

let uid2 = Symbol.for("uid");
console.log(Symbol.keyFor(uid2));   // "uid"

let uid3 = Symbol("uid");
console.log(Symbol.keyFor(uid3));   // undefined

注意,uid和uid2都返回了"uid"这个键,而在Symbol全局注册表中不存在uid3这个Symbol,也就是不存在与之有关的键,所以最终返回undefined。

Symbol全局注册表是一个类似全局作用域的共享环境,也就是说你不能假设目前环境中存在哪些键。当使用第三方组件时,尽量使用Symbol键的命名空间以减少命名冲突。举个例子,jQuery的代码可以为所有键添加"jquery"前缀,就像"jquery.element"或其他类似的键。

w3ctech微信

扫码关注w3ctech微信公众号

共收到0条回复