众所周知,在
JS
中有六种基本数据类型,即五个原始数据类型Undefined
,Null
,Boolean
,String
,Number
和一个对象类型Object
。在
ES6
中,又推出了一种新的原始数据类型,就是我们今天博客中要介绍的——Symbol
类型。这种新的数据类型是做什么用的呢?它有哪些需要我们及时了解和掌握的知识和特性呢?
就让我们来一步一步揭开
ES6
新数据类型Symbol
的神秘面纱吧。
Symbol,就是符号,标志的意思。在ES6
新出现的Symbol
数据类型,主要有以下两个作用:
- 提供
JS
中一直比较欠缺的一种特性: 唯一性 - 为操作
JS
的相关内部逻辑提供接口,也就是操作一些JS
的语言内部行为
当然,第二个作用一般在实际业务和项目中是很难遇到应用的地方的(别告诉我你有在项目代码中进行JS
魔改的打算。。。。
所以这篇博客里对Symbol
的介绍,也主要侧重于它的第一个作用,也就是它所能提供的唯一性, 具体是个什么东西。
Symbol的创建
创建一个Symbol
类型的值的语法如下:
1 | Symbol([description]) |
通过Symbol()
这个静态函数,我们可以创建一个symbol
类型的值。
需要注意的是,跟其他的原始类型不同,创建出来的这个symbol
值我们是看不见的,这也是symbol
对于很多初学者来说比较难以理解的地方,它并不像一个字符串或者数字一样,非常直观的静静的躺在代码里,它是隐藏在代码后面的。我们知道Symbol()
函数返回一个symbol
值,我们也可以将这个symbol
值赋值给一个变量,但我们并不能真真切切的看到这个symbol
值。
1 | let a = Symbol() |
到这里,我们就应该可以理解symbol
值的创建方法了,也明白了我们每次调用Symbol()
创建的每个symbol
值,都是独一无二,仅此一份的,绝对不会重复。
那问题又来了,虽然我们在上面代码中创建的两个symbol
值是不相等的,独一无二的,但打印到控制台,输出的都是Symbol()
。我们怎么区分它们呢?谁知道它们是不是代表的同一个symbol
。
别忘了我们在上面提到的,Symbol
函数可以接受一个可选参数description
。这个参数,就可以为我们创建的symbol
值,提供一个描述,便于我们进行区分。
1 | let a = Symbol('a symbol') |
但要记得我们上面提到的,Symbol()
函数每次创建的symbol
值都是唯一的,description
参数只是提供一个描述。
就算为两次创建的symbol
提供相同的描述,它们各自仍然是独一无二的。切记:symbol
的唯一性唯一性唯一性。
1 | let a = Symbol('tsymbol') |
以上就是symbol
的基本概念,关于它,还有以下几个细碎的要点需要注意:
作为原始数据类型,
symbol
值是不能用new Symbol()
创建的。(你问new Boolean
,new Number()
为什么可以?额,这属于JS
的历史遗留问题,其实它们也不是完全可以,不信你打印一下typeof new Number(22)
symbol
值不能和其他类型的值进行运算1
2
3
4
5let a = Symbol()
+a //TypeError: can't convert symbol to number
a + 3 //TypeError: can't convert symbol to string
a + 'aaa' //TypeError: can't convert symbol to stringsymbol
可以显式转换为字符串1
2
3
4let a = Symbol('a')
String(a) //'Symbol(a)'
a.toString //'Symbol(a)'symbol
可以转换为布尔值1
2
3
4
5
6
7let a = Symbol('a')
!a //false
Boolean(a) //true
//也可以像下面这样使用(谁会这么用啊摔
if(a){....}
Symbol的使用
作为属性名使用
在上面提到symbol
的唯一性这个特点时,相信很多同学就会想到将其作为属性名使用,从而避免我们经常遇到的,在向对象添加属性时错误覆盖对象原有属性等各种有关属性冲突的问题。
通过将symbol
值作为对象的属性名,保证这个对象属性的唯一性,也正是symbol
最重要的作用之一。
1 | let star = Symbol('star') |
需要注意的是,对象以symbol
作为属性名时,访问这个属性必须要使用方括号形式,而不能使用点运算符形式,因为点运算符会始终使用它后面跟随属性名的字符串形式值。
1 | obj[star] //'hello symbol' |
另外,对象以symbol
作为属性名时,该属性依然是公开属性,但不会被for...in
,for...of
循环到,也不会被Object.keys()
,Object.getOwnPropertyNames()
,JSON.stringify()
返回。
要想遍历出对象属性名为symbol
值类型的属性,需要使用Object.getOwnPropertySymbols()
方法,它返回所有symbol
类型属性名的一个数组。
作为常量使用
很多时候,我们需要为对象设置一系列属性用于对应不同的类型,在使用时,我们并不在意这些属性的具体值,只需要保证它们是可区分的各自唯一值即可。此时我们可以使用symbol
作为我们的属性值,从而保证多个属性值间的唯一性。
1 | let type = { |
这样,我们就不需要使用一堆字符串,来区分对象多个属性之间的值了。
Symbol的静态方法
Symbol
有两个方法,分别是Symbol.for(key)
和 Symbol.keyFor(sym)
。
Symbol.for(key)
Symbol.for(key)
方法同样是用于创建一个symbol
值,但它接受一个key
值。如果我们使用相同的key
创建过一个symbol
值,它就会直接返回这个symbol
值,否则就会将创建一个新的以key
为索引登记在全局注册表中的symbol
值。
Symbol
的全局注册表,就是使用Symbol.for(key)
方法创建的所有symbol
的登记薄,它存在于全局环境,甚至可以跨越iframe
的限制。为我们复用具有唯一性的symbol
值提供了非常好的支持。
需要注意,使用Symbol()
创建的symbol
是不会被登记到全局注册表的,它每次都返回一个新的symbol
值。
Symbol.keyFor(sym)
很显然,这个方法就是Symbol.for(key)
的逆方法,它接受一个symbol
值作为参数,并返回这个symbol
在全局注册表中对应的key
值。如果没有对应的key
,它就返回个undefine
(要不它还能返回什么。。。?
通过这两个方法的相互配合,我们对Symbol
的基本使用能够形成一个闭环。
Symbol的静态属性
Symbol
的静态属性,主要用来提供给我们一些操纵js
内部语言行为的一些接口。例如Symbol.replace
,就是用于修改字符串的String.prototype.replace()
方法的。当一个对象被String.prototype.replace()
方法调用时,会使用对象的Symbol.replace
属性对应的方法代替。从而达到修改js
内部语言实现的目的。
1 | let aStr = 'some string' |
具体的,我们就可以写出下面这样的代码。
1 | let x = {}; |
当然,这种Symbol
的使用方式很少被用到,所以在此也不再赘述。有想要了解的同学,为大家提供以下两个链接来对Symbol
做更深入的学习。
好啦,关于ES6
的Symbol
就为大家介绍到这里啦。谢谢,鞠躬退场^-^。