javascript中symbol类型的应用场景(意义)和使用方法
ES中引入了一种新的基础数据类型:Symbol,不过很多开发者可能都不怎么了解 它,或者觉得在实际开发工作中并没有什么场景使用到它,那么我们今天来讲讲这个数据类型,并看看我们怎么来利用它来改进一下我们的代码.
# 这是一种新的基础数据类型(primitive type)
Symbol是由ES6规范一如的一项新特性,它的功能类似于一种标识唯一性的ID.通常情况下,我们可以通过调用Symbol()
函数来创建一个Symbol示例
let a= Symbol()
或者,你也可以在调用`Symbol()函数时传入一个可选的字符串参数,相当于给你创建的Symbol实例一个描述信息:
let b = Symbol('一个b')
如果用当下比较流行的TypeScript的方式来描述这个Symbol()函数的话,可以表示成:
/**
* @param {any} description 描述信息,可以是任何可以被转换成字符串的值,如:字符串、数字、对象、数组等
*/
function Symbol(description?:any):symbol
2
3
4
由于Symbol是一种基础数据类型,所以我们使用typeof去检查它的类型的时候,它会返回一个属于自己的类型Symbol,而不是什么string、object之类的:
typeof a // 'symbol'
另外,我们需要重点记住的一点是:每个Symbol实例都是唯一的.因此,当你比较连哥哥symbol实例的时候,将总会返回false:
let a = Symbol()
let b = Symbol('1')
let c = Symbol('2')
a===b //false
b===c //false
2
3
4
5
# 应用场景 : 使用Symbol来作为对象属性名(key)
在这之前,我们通常定义或访问对象的属性都是使用字符串,比如下面的代码:
let obj = {
abc:123,
'hello':'world'
}
obj[abc] //123
obj[hello] //'world'
//而现在,Symbol可以同样用于对象属性的定义和访问:
const PROP_NAME = Symbol()
const PROP_AGE = Symbol()
let obj = (
[PROP_NAME]:'一斤代码'
)
obj[PROP_NAME]= 18
obj[PROP_NAME]// 一斤代码
obj[PROP_AGE] // 10
2
3
4
5
6
7
8
9
10
11
12
13
14
15
随之而来的是另一个非常值得注意的问题:就是当使用了Symbol作为对象的属性key后,在对改对象进行key的枚举时,会有什么不同?在实际应用中,我们经常会需要使用Object.keys()或者for...in来枚举对象的属性名,那在这方面,Symbol类型的key表现的会有什么不同之处呢?来看看以下示例代码:
let obj = {
[Symbol('name')]:'一斤代码'
age:19,
title:'哈哈哈'
}
Object.keys(obj) // ['age','title']
for(let p in obj ){
console.log(p) //分别会输出‘age'和‘title’
}
Object.getOwnPropertyName(obj) // ['age','title']
2
3
4
5
6
7
8
9
10
由上代码可知,Symbol类型的key是不能通过Object.keys()或者for...in来枚举的,它未被包含在对象自身的属性名集合(propety names)之中,所以利用该特性,我们可以把一些不需要对外操作和访问的属性使用Symbol来定义;
也正是因为这样一个特性,当使用JSON.stirngfiy()将对象转换成JSON字符串的时候,Symbol属性也被排除在输出内容之外:
JSON.stringfiy(obj)// {'age':18,'title':'哈哈哈'}
我们可以利用这一特点来更好的设计我们的数据对象,让"对内操作"和'对外选择性输出'变得更加优雅.
然而,这样的话,我们就没办法获取Symbol方式定义的对象属性了么?非也.还是会有一些专门针对Symbol的API,比如
// 使用Object的API
Object.getOwnPropertySymbols(obj) //Symbol(name)
//使用新增的反射API
Reflect.ownKeys(obj)// [Symbol(name),’age‘,‘title’]
2
3
4
# 应用场景2:使用Symbol来代替常量
先来看一下代码,是不是经常在你的代码里出现?
const TYPE_AUDIO= 'AUDIO'
const TYPE_VIDEO= 'VIDEO'
const TYPE_IMAGE= 'IMAGE'
function handleFileResource(resource){
switch(resource.type){
case TYPE_AUDIO :
playAudio(resource)
break;
case TYPE_VIDEO :
playVideo(resourse)
break;
case TYPE_IMAGE :
previewImage(resourse)
break;
default :
throw new Error('未知类型resourse')
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
如上面的代码中的那样 ,我们经常定义一组常量来代表一种业务逻辑下的几个不同类型,我们通常希望这几个常量之间的唯一关系,为了保证这一点,我们需要为常量赋一个唯一的值(比如这里的AUDIO,VIDEO,IMAGE),常量少的时候还算好,但是常量一多,你可能还得花点脑子好好为他们取个好点的名字.
现在有了Symbol,我们大可不必这么麻烦了:
const TYPE_AUDIO = Symbol()
const TYPE_VIDEO = Symbol()
const TYPE_IMAGE = Symbol()
2
3