由于 Symbol 常量 PASSWORD 被定义在 a.js 所在的模块中,外面的模块获取不到这个 Symbol,也不可能再创建一个一模一样的 Symbol 出来(因为 Symbol 是唯一的),因此这个 PASSWORD 的 Symbol 只能被限制在 a.js 内部使用,所以使用它来定义的类属性是没有办法被模块外访问到的,达到了一个私有化的效果。
1.2 注册和获取全局 Symbol
通常情况下,我们在一个浏览器窗口中(window),使用 Symbol()函数来定义 Symbol 实例就足够了。
但是,如果你的应用涉及到多个 window(最典型的就是页面中使用了<iframe>),并需要这些 window 中使用的某些 Symbol 是同一个,那就不能使用Symbol()函数了,因为用它在不同 window 中创建的 Symbol 实例总是唯一的,而我们需要的是在所有这些 window 环境下保持一个共享的 Symbol。这种情况下,我们就需要使用另一个 API 来创建或获取 Symbol,那就是Symbol.for(),它可以注册或获取一个 window 间全局的 Symbol 实例:
let gs1 = Symbol.for("global_symbol_1"); // 注册一个全局Symbol
let gs2 = Symbol.for("global_symbol_2"); // 获取全局Symbol
gs1 === gs2; // true
这样一个 Symbol 不光在单个 window 中是唯一的,在多个相关 window 间也是唯一的了。
1.3 一些需要特别注意的点
Symbol 函数前不能使用 new 命令,否则会报错。
Symbol 函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。
Symbol 作为属性名,该属性不会出现在 for...in、for...of 循环中,也不会被 Object.keys()、Object.getOwnPropertyNames()、JSON.stringify() 返回。
Object.getOwnPropertySymbols 方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。
Symbol.for 接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建并返回一个以该字符串为名称的 Symbol 值。
扫描下方二维码,收藏关注,及时获取答案以及详细解析,同时可解锁800+道前端面试题。
Symbol
ES6 中引入了一种新的基础数据类型:Symbol,这是一种新的基础数据类型(primitive type)。
它的功能类似于一种标识唯一性的 ID。通常情况下,我们可以通过调用 Symbol()函数来创建一个 Symbol 实例:
或者,你也可以在调用 Symbol()函数时传入一个可选的字符串参数,相当于给你创建的 Symbol 实例一个描述信息:
如果用当下比较流行的 TypeScript 的方式来描述这个 Symbol()函数的话,可以表示成:
由于 Symbol 是一种基础数据类型,所以当我们使用 typeof 去检查它的类型的时候,它会返回一个属于自己的类型 symbol,而不是什么 string、object 之类的:
另外,我们需要重点记住的一点是:每个 Symbol 实例都是唯一的。因此,当你比较两个 Symbol 实例的时候,将总会返回 false:
1.1 一些应用场景
在这之前,我们通常定义或访问对象的属性时都是使用字符串,比如下面的代码:
而现在,Symbol 可同样用于对象属性的定义和访问:
随之而来的是另一个非常值得注意的问题:就是当使用了 Symbol 作为对象的属性 key 后,在对该对象进行 key 的枚举时,会有什么不同?在实际应用中,我们经常会需要使用
Object.keys()
或者for...in
来枚举对象的属性名,那在这方面,Symbol 类型的 key 表现的会有什么不同之处呢?来看以下示例代码:由上代码可知,Symbol 类型的 key 是不能通过
Object.keys()
或者for...in
来枚举的,它未被包含在对象自身的属性名集合(property names)之中。所以,利用该特性,我们可以把一些不需要对外操作和访问的属性使用 Symbol 来定义。也正因为这样一个特性,当使用 JSON.stringify()将对象转换成 JSON 字符串的时候,Symbol 属性也会被排除在输出内容之外:
我们可以利用这一特点来更好的设计我们的数据对象,让“对内操作”和“对外选择性输出”变得更加优雅。
然而,这样的话,我们就没办法获取以 Symbol 方式定义的对象属性了么?非也。还是会有一些专门针对 Symbol 的 API,比如:
看一下下面的代码是不是非常熟悉
如上面的代码中那样,我们经常定义一组常量来代表一种业务逻辑下的几个不同类型,我们通常希望这几个常量之间是唯一的关系,为了保证这一点,我们需要为常量赋一个唯一的值(比如这里的'AUDIO'、'VIDEO'、 'IMAGE'),常量少的时候还算好,但是常量一多,你可能还得花点脑子好好为他们取个好点的名字。
现在有了 Symbol,我们大可不必这么麻烦了:
这样定义,直接就保证了三个常量的值是唯一的了!是不是挺方便的呢。
我们知道在 JavaScript 中,是没有如 Java 等面向对象语言的访问控制关键字 private 的,类上所有定义的属性或方法都是可公开访问的。因此这对我们进行 API 的设计时造成了一些困扰。
而有了 Symbol 以及模块化机制,类的私有属性和方法才变成可能。例如:
在文件 a.js 中
在文件 b.js 中
由于 Symbol 常量 PASSWORD 被定义在 a.js 所在的模块中,外面的模块获取不到这个 Symbol,也不可能再创建一个一模一样的 Symbol 出来(因为 Symbol 是唯一的),因此这个 PASSWORD 的 Symbol 只能被限制在 a.js 内部使用,所以使用它来定义的类属性是没有办法被模块外访问到的,达到了一个私有化的效果。
1.2 注册和获取全局 Symbol
通常情况下,我们在一个浏览器窗口中(window),使用 Symbol()函数来定义 Symbol 实例就足够了。
但是,如果你的应用涉及到多个 window(最典型的就是页面中使用了
<iframe>
),并需要这些 window 中使用的某些 Symbol 是同一个,那就不能使用Symbol()
函数了,因为用它在不同 window 中创建的 Symbol 实例总是唯一的,而我们需要的是在所有这些 window 环境下保持一个共享的 Symbol。这种情况下,我们就需要使用另一个 API 来创建或获取 Symbol,那就是Symbol.for()
,它可以注册或获取一个 window 间全局的 Symbol 实例:这样一个 Symbol 不光在单个 window 中是唯一的,在多个相关 window 间也是唯一的了。
1.3 一些需要特别注意的点
for...in
、for...of
循环中,也不会被Object.keys()
、Object.getOwnPropertyNames()
、JSON.stringify()
返回。Object.getOwnPropertySymbols
方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。Symbol.for
接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建并返回一个以该字符串为名称的 Symbol 值。Symbol.keyFor
方法返回一个已登记的 Symbol 类型值的 key。