深入理解Symbol和Symbol属性 | 青训营

180 阅读4分钟

1 背景和简介

  • ES6引入了第6种原始类型:Symbol。 起初,人们用它来创建对象的私有成员,在Symbol出现以前,一直都是通过属性名来访问所有属性,无论属性名由什么元素构成,全部都是通过一个字符串类型的名称来访问的。私有名称原本是为了让开发者们创建非字符串属性名称而设计的,但是一般的技术无法检测这些属性的私有名称。私有名称最终演变成了ES6中的Symbol。虽然通过Symbol可以为属性添加非字符串名称,但是其隐私性被打破了,新标准中将Symbol属性与对象中的其他属性分别分类。
  • symbol 是一种基本数据类型(primitive data type)。Symbol() 函数会返回 symbol 类型的值,该类型具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象;它的静态方法会暴露全局的 symbol 注册,且类似于内建对象类,但作为构造函数来说它并不完整,因为它不支持语法:“new Symbol()”。
  • 每个从 Symbol() 返回的 symbol 值都是唯一的。一个 symbol 值能作为对象属性的标识符;这是该数据类型仅有的目的。

2 创建Symbol

  • 所有原始值,除了Symbol之外都有各自的字面形式,例如布尔类型的true或数字类型的42。可以通过全局的Symbol函数创建一个Symbol,作为对象属性,并用于访问。
// 创建一个名为firstName的Symbol
let firstName = Symbol();
let person = {};
// 用Symbol将一个新的属性赋值给person对象
person[firstName] = "Nocholas";
// 进行访问的时候一定要用到最初定义的Symbol
// Nocholas
console.log(person[firstName]);
// Symbol()
console.log(firstName);
  • 在上面这段代码中,创建了一个名为firstName的Symbol,用它将一个新的属性赋值给person对象,每当想访问这个属性时一定要用到最初定义的Symbol。记得要合理命名Symbol变量,这样可以轻松区分出它所指代的内容。
  • Symbol的描述被存储在内部的[[Description]]属性中,只有当调用Symbol的toString()方法时才可以读取这个歌属性。在执行console.log()时隐式调用了firstName的toString()方法,所以它的描述会被打印到日志中,但不能直接在代码里访问[[Description]]。

Symbol的辨识方法

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

Symbol的使用方法

  • 所有使用可计算属性名的地方,都可以使用Symbol。Symbol可以用于计算对象字面量属性名、Object.defineProperty方法和Object.defineProperties方法的调用过程中。
  • 尽管在所有可计算属性名的地方,都可以使用Symbol来代替,但是为了在不同代码片段间有效地共享这些Symbol,需要建立一个体系。

Symbol的共享体系

  • 有时候我们希望在不同的代码中共享同一个Symbol,例如,在你的应用中有两种不同的对象类型,但是你希望它们使用同一个Symbol属性来表示一个独特的标识符。一般而言,在很大的代码库中或跨文件追踪Symbol非常困难而且容易出错,出于这些原因,ES6提供了一个可以随时访问的全局Symbol注册表。
  • Symbol全局注册表是一个类似全局作用域的共享环境,也就是说你不能假设目前环境中存在哪些键。当使用第三方组件时,尽量使用Symbol键的命名空间以减少命名冲突。举个例子,jQuery的代码可以为所有键添加"jquery"前缀,就像"jquery.element"或其他类似的键。

总结

Symbol是JS中的一个新的原始类型,用于创建必须通过Symbol才能引用的属性。尽管这些属性不是完全私有的,但是它们比较难以被意外覆写而改变,如此一来,对于开发者而言,这些属性非常适合用于那些需要一定程度保护的功能。