ECMAScript 6 笔记(12)- Symbol

原创 乘风逐月 随笔 ES6 170阅读 2018-09-06 15:23:41 举报

一、概述

ES5 的对象属性名都是字符串,这容易造成属性名冲突。ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型。前六种是:undefined、null、Boolean、String、Number、Object。

Symbol 值通过 Symbol 函数生成。这就是说,对象的属性名现在有两种类型,一种是字符串,一种是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就是独一无二的,可以保证不会与其他属性名冲突。

1. 基本用法

注: Symbol 函数前不能使用 new 命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象,即也不能添加属性。

Symbol 函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时比较容易区分。

不加参数,打印出来都是 "Symbol()" ,不利于区分,加上参数,等于有了描述,输出的时候就可以区分是哪一个的值。

2.注意问题

(1)Symbol 函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的 Symbol 函数的返回值是不相等。

(2)Symbol 值不能与其他类型的值进行运算,会报错。

(3)Symbol 值可以转为布尔值,但不能转为数值。

二、作为属性名的 Symbol

由于每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符,对于对象的属性名,就能保证不会出现同名的属性。
这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。

注意:
(1)Symbol 值作为属性名时,不能用点运算符。

上例中,因为点运算后面总是字符串,所有不会读取到 'hello' ,而是 undefined。
(2)在对象内部,用 Symbol 值定义属性时,Symbol 值必须放在方括号之中。如果不放在方括号中,该属性名就是字符串,而不知 Symbol 值。
(3)Symbol 值作为属性名时,该属性还是公开属性,不是私有属性。

Symbol 类型还可以用于定义一组常量,保证这组常量的值都是不相等的。
常量使用 Symbol 值对打的好处,就是其他任何值都不可能有相同的值了。

三、属性名的遍历

Symbol 作为属性名,该属性不会出现在 for...in、for...of循环中,也不会被 Object.keys()、Object.getOwnPropertyName、JSON.stringify()返回。但是它不是私有属性。

Object.getOwnPropertySymbols() 方法,可以获取指定对象的所有 Symbol 属性名。该方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。
Reflect.ownkeys() 方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。

由于以 Symbol 值作为名称的属性,不会被常规方法遍历得到。可以利用这个特性,为对象定义一些非私有的、但又只希望内部使用的属性方法。

四、Symbol.for()、Symbol.keyFor()

1.Symbol.for()

如果希望重新使用同一个 Symbol 值,Symbol.for() 方法可以做到这一点。他接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,如果没有就新建并返回以该字符串为名称的 Symbol 值。

Symbol.for() 与 Symbol() 的区别:
两个方法都会生成新的 Symbol。但是 Symbol.for() 不会每次都会生成新值,而是先检查给定的 key 是否存在,如果不存在才会新建一个值。但是 Symbol() 每次都会新建一个值,没有登记机制。

注: Symbol.for() 为 Symbol 值登记的名字,是全局环境的,可以在不同的 iframe 或 service worker 中取到同一个值。

2. Symbol.keyFor()

Symbol.keyFor() 方法返回一个已经登记的 Symbol 类型值的 key。

上例中 s1 属于未登记的 Symbol 值,所以返回 undefined。

五、内置的 Symbol 值

除了定义自己使用的 Symbol 值以外,ES6 还提供了11个内置的 Symbol 值,指向语言内部使用的方法。

1.Symbol.hasInstance

对象的 Symbol.hasInstance 属性,指向一个内部方法。当其他对象使用 instanceof 运算符,判断是否该为该对象的实例时,会调用这个方法。如,foo instanceof Foo 在语言内部,实际调用的是 FooSymbol.hasInstance

上例中,new MyClass() 会返回一个实例。该实例的 Symbol.hasInstance 方法,会在进行 instanceof 运算时自动调用,判断左侧的数组是否是 Array 的实例。

2.Symbol.isConcatSpreadable

对象的 Symbol.isConcatSpreadable 属性等于一个布尔值,表示该对象用于 Array.prototype.concat() 时,是否可以展开。

上例说明,数组的默认行为是可以展开的,Symbol.isConcatSpreadable 默认等于 undefined。该属性为 true 时,数组可以展开,为 false 时不可以展开。

类似数组的对象正好相反,默认不展开。它的 Symbol.isConcatSpreadable 属性为 true,才可以展开。

Symbol.isConcatSpreadable 属性也可以定义在类里面。

上例中 A1 可以展开,A2 不可以展开。A1 的 Symbol.isConcatSpreadable 属性定义在实例上,A2 定义在类本身。

3.Symbol.species

对象的 Symbol.species 属性,指向一个构造函数。创造衍生对象时,会使用该属性。

上例中,定义了 Symbol.species 属性,创建衍生对象时就会使用这个属性返回的函数,作为构造函数。
默认的 Symbol.species 属性等同于下面的写法。

注意:定义 Symbol.species 属性要采用 get 取值器。

上例中,衍生对象 b 不是 MyArray 的实例,而是 Array 的实例。
Symbol.species 的作用在于,实例对象在运行过程中,需要再次调用自身的构造暗函数时,会调用该属性指定的构造函数。它的主要用途是,有些类库是在基类的基础上修改的,那么子类使用继承的方法时,作者可能希望返回基类的实例,而不是子类的实例。

评论 ( 0 )
最新评论
暂无评论

赶紧努力消灭 0 回复