ES5 的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin 模式),新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是 ES6 引入Symbol的原因。
JS中分为七种内置类型,七种内置类型又分为两大类型:基本类型、对象(Object)。基本类型有6种,分别是:null、undefined、boolean、number、string、Symbol。其中Symbol是ES6新添加的数据类型。
Symbol表示独一无二的。Symbol值通过Symbol函数生成。因此对象的属性名现在可以有两种类型,一种是我们原来一直都使用的字符串类型,另一种就是新增的Symbol类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。比如:let s = Symbol();
此时变量s就是一个独一无二的值。当我们使用typeof运算符的时候,此时变量s是Symbol数据类型。typeof s; // "symbol"
注意:我们在定义Symbol的时候,在Symbol函数前不能使用new命令,否则会报错。其原因是因为生成的Symbol是一个原始类型的值,而不是对象。由于Symbol值不是对象,因此不能添加属性。
Symbol函数可以接受一个字符串作为参数,表示对Symbol实例的描述。
1 | let s1 = Symbol('foo'); |
针对于上面的代码,s1和s2是两个 Symbol 值。如果不加参数,它们在控制台的输出都是Symbol(),不利于区分。有了参数以后,就等于为它们加上了描述,输出的时候就能够分清,到底是哪一个值。
如果 Symbol 的参数是一个对象,就会调用该对象的toString方法,将其转为字符串,然后才生成一个 Symbol 值。
1 | const obj = { |
有参数与没有参数的情况:
注意:Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的。
1 | // 没有参数的情况 |
上面代码中,s1和s2都是Symbol函数的返回值,而且参数相同,但是它们是不相等的。
Symbol 值不能与其他类型的值进行运算,会报错。
1 | let s = Symbol('foo'); |
需要注意的是:Symbol值可以显示转换为字符串。
1 | let s = Symbol('foo'); |
我们还需要注意一点,Symbol 值也可以转为布尔值,但是不能转为数值。
1 | let s = Symbol('foo'); |
1 | let mySymbol1 = Symbol("foo1"); |
注意:在这里不可以使用点运算符,因为点运算符后面总是字符串。:
1 | let mySymbol4 = Symbol("foo4"); |
说了这么久,那么我们到底为什么要使用Symbol呢?下面给大家一个场景:我们想区分两个属性,其实我们并不在意,这两个属性值究竟是什么,我们在意的是,这两个属性绝对要区分开来。比如:我们想计算不同形状的面积,由于不同形状需要用不同的计算面积的公式来进行相应的运算。当我们不使用Symbol时,代码如下所示:
1 | let shape_type = {rectangle: "Rectangle"}; |
在这里使用rectangle的名字叫做”Rectangle”,其实真正意义上我们不想对rectangle去特地的去一个名字,我们只想要区分rectangle这个形状不同于任何其他形状,那么这个时候Symbol就派上用场了。
1 | const shape_type = { |
也就是说,我们不用非要去给变量赋一个字符串的值,去区分它和别的变量的值不同,因为去给每个变量取个语义化而又不同的值是一件伤脑子的事,当我们只需要知道每个变量的值都是百分百不同的即可,这时候我们就可以用Symbol。