ES6引入了第6种原始类型:Symbol。起初,人们用它来创建对象的私有成员;在Symbol出现之前,人们一直通过属性名来访问所有属性,无论属性名由什么元素构成,全部通过一个字符串类型的名称来访问。
私有名称原本是为了让开发者创建非字符串属性名称而设计的,但是一般的技术无法检测这些属性的私有名称。
创建Symbol
可以通过全局的Symbol函数创建一个Symbol:
|
|
由于Symbol是原始值,因此调用new Symbol()会导致程序抛出错误。
Symbol接受一个可选参数,其可以添加一段文本描述即将创建的Symbol,这段描述不可以用作属性访问,只是为了便于阅读和调试Symbol程序;symbol的描述被存储在内部的[[Description]]属性中,只有当调用Symbol的toString()方法时才可以读取这个属性;
检测一个Symbol:
Symbol的使用方法
所有使用可计算属性名的地方,都可以使用Symbol。如Object.defineProperty等:
|
|
Symbol共享体系
在不同的代码中共享同一个Symbol,ES6提供了一个可以随时访问的全局Symbol注册表;如果要创建一个共享的Symbol,要使用Symbol.for方法,它只接受一个参数,也就是即将创建的Symbol的字符串标示,这个参数同样也被用作Symbol的描述;
|
|
Symbol.for首先在全局Symbol注册表中搜索键为’uid’的Symbol,如果存在,直接返回已存在的,否则创建一个新的Symbol,并使用这个键在Symbol全局注册表中注册,随机返回新创建的Symbol。
|
|
还有一个与Symbol共享有关的特性,Symbol.keyFor方法在Symbol全局注册表中检索与Symbol有关的键;
|
|
Symbol全局注册表中不存在uid3这个Symbol(没有通过Symbol.for注册),返回undefined;
Symbol与类型强制转换
不能将Symbol强制转换为字符串和数字类型;Symbol在需要逻辑判断表达式中为真;
|
|
Symbol属性检索
Object.keys和Object.getOwnpropertyNames不能检索出Symbol属性,ES6中Object.getOwnpropertySymbols来支持这个功能,返回对象中的所有Symbol属性的数组;
|
|
通过well-known Symbol暴露内部操作
ES6通过在原型链上定义与Symbol相关的属性来暴露更多的语言内部逻辑;开放了以前JS中常见的内部操作,并通过预定义一些well-known Symbol来表示。每一个这类Symbol都是symbol对象的一个属性,例如Symbol.match
Symbol.hasInstance
一个在执行instanceof时调用的内部方法,用于检测对象的继承信息;
Function.prototype中有一个Symbol.hasInstance方法,每一个函数都继承了这个方法,用于确定对象是否是函数的实例。该方法被定义为不可写,不可配置并且不可枚举。
|
|
看一段代码:
从上面可以看出Array是Function的实例;Function.prototype具有自有属性Symbol.hasInstance,Array没有Symbol.hasInstance;
obj instanceof Array
实际等价于Array[Symbol.hasInstance](obj)
,由于Array没有Symbol.hasInstance
,所以会从原型链中寻找,最终在Function.prototype
中找到,最终实际执行的就是Function.prototype[Symbol.hasInstance](obj)
。
明白了instanceof的实际调用过程,就可以按我们的意愿来定制一些内容,例如定义一个无实例的函数:
|
|
Symbol.isConcatSpreadable
concat元素
concat数组
JS规范声明,concat凡是传入了数组参数,就会自动将它们分解成独立元素,在ES6之前,我们根本无法调整这个特性;
Symbol.isConcatSpreadable属性是一个布尔值,如果该属性为true,则表示对象由length属性和数字键,故它的数值型属性应该被独立添加到cancat()调用结果中;与其他well-known Symbol属性不同的是,这个Symbol属性默认情况下不会出现标准对象,它只是一个可选对象,用于增强作用于特定对象类型的concat()方法的功能,有效简化其默认特性;
|
|
Symbol.match Symbol.replace Symbol.search Symbol.split
这四个Symbol用在String对应的四个方法String.match/replace/search/split中且当参数为正则表达式时。
可以定义一个对象的这四个Symbol属性方法,这对象就可以代替正则表达式来应用在上面的对应场景中;
|
|
Symbol.toPrimitive
JS引擎中,经常会尝试将对象转换到相应的原始值,例如比较一个对象与字符串;对象被转换为原始值,会有三种选择:数字,字符串或无类型偏好的值。对于大多数标准对象,当对象被转换为数字时:
- 调用valueOf方法,如果结果为原始值,则返回
- 否则,调用toString方法,如果结果值为原始值,则返回
- 如果再无可选值,则抛出异常
当对象被转换为字符串时:
- 调用toSring方法,如果结果为原始值,则返回
- 否则,调用valueOf方法,如果结果值为原始值,则返回
- 如果再无可选值,则抛出异常
大多数情况下,标准对象会将默认模式按数字模式处理(除了Date对象);默认模式只用于==运算,+运算及给Date构造函数传递一个参数时。
在ES6标准中,通过Symbol.toPrimitive方法可以更改那个暴露出来的原始值;Symbol.toPrimitive被定义在每一个标准类型的原型上,并且规定了当对象被转换为原始值时应该执行的操作。
|
|
Symbol.toStringTag
当检测一个数据的类型时,可能会用instanceof,这样在使用iframe标签情况下会有一定问题,不同的iframe代表不同的领域,不同领域的构造函数是不同的,当数据传递到不同的领域时,使用instanceof会得不到预期的结果;
这时我们可能会用Object.prototype.toString.call来处理;
如区分原生JSON对象和自建对象
在ES6中重新定义了原生对象过去的状态,通过Symbol.toStringTag改变了调用Object.prototype.toString方法时返回的身份标示;这个Symbol所代表的属性在每一个对象中都存在,其定义了调用对象的Object.prototype.toString.call方法时返回的值。
|
|
Symbol.unscopables
The Symbol.unscopables well-known symbol is used to specify an object value of whose own and inherited property names are excluded from the with environment bindings of the associated object.
|
|
也可以改变对象的Symbol.unscopables属性:
|
|