ECMAScript 6 笔记(12)- Symbol

原创 乘风逐月 随笔 ES6 197阅读 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 的作用在于,实例对象在运行过程中,需要再次调用自身的构造暗函数时,会调用该属性指定的构造函数。它的主要用途是,有些类库是在基类的基础上修改的,那么子类使用继承的方法时,作者可能希望返回基类的实例,而不是子类的实例。

4.Symbol.match

对象的 Symbol.match 属性,指向一个函数。当执行 str.macth(myObject) 时,如果该属性存在,会调用它,返回该方法的返回值。

5.Symbol.replace

对象的 Symbol.replace 属性,指向一个方法,当该对象被 String.prototype.replace 方法调用时,会返回该方法的返回值。

Symbol.replace 方法会收到两个参数,第一个参数是 replace 方法正在作用的对象,即 'hello',第二个参数是替换后的值,即 'world'。

6.Symbol.search

对象的 Symbol.search 属性,指向一个方法,当该对象被 String.prototype.search 方法调用时,会返回该方法的返回值。

7.Symbol.split

对象的 Symbol.split 属性,指向一个方法,当该对象被 String.prototype.split 方法调用时,会返回该方法的返回值。

8.Symbol.iterator

对象的 Symbol.iterator 属性,指向该对象的默认遍历器方法。对象进行 for...of 循环时,会调用 Symbol.iterator 方法,返回该对象的默认遍历器。

9.Symbol.toPrimitive

对象的 Symbol.toPrimitive 属性,指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。
Symbol.toPrimtive 被调用时,会接受一个字符串参数,表示当前运算的模式,一共有三种模式:
-Number: 该场合需要转成数值
-String: 该场合需要转成字符串
-Default: 该场合可以转成数值,也可以转成字符串

10.Symbol.toStringTag

对象的 Symbol.toStringTag 属性,指向一个方法。在该对象上面调用 Object.prototype.toString 方法时,如果这个属性存在,它的返回值会出现在 toString 方法返回的字符串之中,表示对象的类型。也就是说,这个属性可以用来定制[Object Objetc]或[Object Array] 中 object 后面的那个字符串。

ES6 新增内置对象的 Symbol.toStringTag 属性值如下:
-JSON[Symbol.toStringTag]:'JSON'
-Math[Symbol.toStringTag]:'Math'
-Module 对象M[Symbol.toStringTag]:'Module'
-ArrayBuffer.prototype[Symbol.toStringTag]:'ArrayBuffer'
-DataView.prototype[Symbol.toStringTag]:'DataView'
-Map.prototype[Symbol.toStringTag]:'Map'
-Promise.prototype[Symbol.toStringTag]:'Promise'
-Set.prototype[Symbol.toStringTag]:'Set'
-WeakMap.prototype[Symbol.toStringTag]:'WeakMap'
-WeakSet.prototype[Symbol.toStringTag]:'WeakSet'
-Symbol.prototype[Symbol.toStringTag]:'Symbol'
-Generator.prototype[Symbol.toStringTag]:'Generator'
-GeneratorFunction.prototype[Symbol.toStringTag]:'GeneratorFunction'

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

赶紧努力消灭 0 回复