javascript中symbol类型的应用场景(意义)和使用方法

2022/10/28 js

ES中引入了一种新的基础数据类型:Symbol,不过很多开发者可能都不怎么了解 它,或者觉得在实际开发工作中并没有什么场景使用到它,那么我们今天来讲讲这个数据类型,并看看我们怎么来利用它来改进一下我们的代码.

# 这是一种新的基础数据类型(primitive type)


Symbol是由ES6规范一如的一项新特性,它的功能类似于一种标识唯一性的ID.通常情况下,我们可以通过调用Symbol()函数来创建一个Symbol示例

let a= Symbol()
1

或者,你也可以在调用`Symbol()函数时传入一个可选的字符串参数,相当于给你创建的Symbol实例一个描述信息:

let b = Symbol('一个b')
1

如果用当下比较流行的TypeScript的方式来描述这个Symbol()函数的话,可以表示成:

/**
 * @param {any} description 描述信息,可以是任何可以被转换成字符串的值,如:字符串、数字、对象、数组等
 */
function Symbol(description?:any):symbol
1
2
3
4

由于Symbol是一种基础数据类型,所以我们使用typeof去检查它的类型的时候,它会返回一个属于自己的类型Symbol,而不是什么string、object之类的:

typeof a // 'symbol'
1

另外,我们需要重点记住的一点是:每个Symbol实例都是唯一的.因此,当你比较连哥哥symbol实例的时候,将总会返回false:

let a = Symbol()
let b = Symbol('1')
let c = Symbol('2')
a===b  //false 
b===c //false
1
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
1
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']
1
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':'哈哈哈'}
1

我们可以利用这一特点来更好的设计我们的数据对象,让"对内操作"和'对外选择性输出'变得更加优雅.

然而,这样的话,我们就没办法获取Symbol方式定义的对象属性了么?非也.还是会有一些专门针对Symbol的API,比如

// 使用Object的API
Object.getOwnPropertySymbols(obj) //Symbol(name)
//使用新增的反射API
Reflect.ownKeys(obj)// [Symbol(name),’age‘,‘title’]
1
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')
  }
}
1
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()
1
2
3
最后更新时间: 2023/1/12 15:59:09