返回 登录
0

JavaScript大师Nicholas C. Zakas又一大作出中文版了

本文节选自Nicholas C. Zakas所著《Understanding EcmaScript6》的中文版《深入理解ES6》,由电子工业出版社出版。
著:【美】Nicholas C. Zakas(尼古拉斯·泽卡斯)
译:刘振涛
责编:陈秋歌,寻求报道或者投稿请发邮件至chenqg#csdn.net。

导读:本文节选自图书《深入理解ES6》第六章,Symbol为ECMAScript 6引入了第6种原始类型,本文将详解如何有效地使用它。

在ECMAScript 5及早期版本中,语言包含5种原始类型:字符串型、数字型、布尔型、null和undefined。ECMAScript 6引入了第6种原始类型:Symbol。起初,人们用它来创建对象的私有成员,JavaScript开发者们对这个新特性期待已久。在Symbol出现以前,人们一直通过属性名来访问所有属性,无论属性名由什么元素构成,全部通过一个字符串类型的名称来访问;私有名称原本是为了让开发者们创建非字符串属性名称而设计的,但是一般的技术无法检测这些属性的私有名称。

私有名称最终演变成了ECMAScript 6中的Symbol,本章将讲解如何有效地使用它。虽然通过Symbol可以为属性添加非字符串名称,但是其隐私性就被打破了。最终,新标准中将Symbol属性与对象中的其他属性分别分类。

创建Symbol

所有原始值,除了Symbol以外都有各自的字面形式,例如布尔类型的true或数字类型的42。可以通过全局的Symbol函数创建一个Symbol,就像这样:

letfirstName = Symbol();
let person = {};

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

在上面这段代码中,创建了一个名为firstName的Symbol,用它将一个新的属性赋值给person对象,每当你想访问这个属性时一定要用到最初定义的Symbol。记得要合理命名Symbol变量,这样可以轻松区分出它所指代的内容。

注意:由于Symbol是原始值,因此调用new Symbol()会导致程序抛出错误。也可以执行new Object(你的Symbol)创建一个Symbol的实例,但目前尚不清楚这个功能何时可以使用。

Symbol函数接受一个可选参数,其可以让你添加一段文本描述即将创建的Symbol,这段描述不可用于属性访问,但是建议你在每次创建Symbol时都添加这样一段描述,以便于阅读代码和调试Symbol程序。

let firstName = Symbol("first name");
let person = {};

person[firstName] = "Nicholas";

console.log("first name" in person); // false
console.log(person[firstName]); // "Nicholas"
console.log(firstName); // "Symbol(first name)"

Symbol的描述被存储在内部的[[Description]]属性中,只有当调用Symbol的toString()方法时才可以读取这个属性。在执行console.log()时隐式调用了firstName的toString()方法,所以它的描述会被打印到日志中,但不能直接在代码里访问[[Description]]。

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()方法的调用过程中。

letfirstName = Symbol("first name");

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

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

letlastName = 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的描述,就像这样:

letuid = 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,像这样:

letuid = 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有关的键。举个例子:

letuid = 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”或其他类似的键。

点击订购:深入理解ES6

图片描述


欢迎加入“CSDN前端开发者”群,与更多专家、技术同行进行热点、难点技术交流。请扫描以下二维码申请入群。
图片描述

评论