Symbol数据类型的延伸之(enum枚举)
导语:Symbol
是一种基本数据类型,每个从Symbol()
返回的值都是唯一的。一个Symbol
的值能作为对象属性的标识符,这是该数据类型仅有的目的。 ---- 《摘自MDN》
敲黑板:唯一的、标识符
这句话值得我们细细咀嚼,我们由上面这句话来展开思考。说到唯一性,我们可以联想到的数据类型有什么?
const
常量TypeScript
枚举enum
TypeScript Symbol
下面我们就这三种数据类型展开对比:
上一回我们说了const
常量,这回我们来说说同样值得我们讨论的,具有相对唯一性的,enum
枚举类型。
一、Enum枚举的数字和字符串枚举
enum
枚举类型:
我们知道,JavaScript是没有枚举类型的,只有TypeScript
有枚举类型。
使用枚举,我们可以定义一些带名字的常量,可以清晰的表达意图或创建一组有区别的用例。 ---- 《摘自TypeScript官网》
- 我们先来看一下数字枚举
enum Direction {
Up = 1,
Down,
Left,
Right
}
如上,我们定义了一个数字枚举,Up
使用初始化为1
。其余的成员会从1
开始自动增长。换句话说,Direction.Up
的值为1
,Down
为2
,Left
为3
,Right
则为4
。
我们也可以完全不使用初始化器,结果和上面是等效的:
enum Direction {
Up,
Down,
Left,
Right
}
使用枚举很简单,通过枚举的属性来访问枚举成员,或是通过枚举的名字来访问枚举类型:
enum Response {
No = 0,
Yes = 1
}
function respond(recipient: string, message: Response): void {
// ...
console.log(`${recipient} is ${message}`)
}
respond("火星", Response.No) // 火星 is 0
这是最常见的枚举形式,但是会有以下的问题:
- 对打印不友好,打印出来的数值通常只有一个数字;
- 类型不安全,枚举值不完全唯一,可以被其他数值干扰;
- 成员无法检查,无法检查枚举值是否是该类型的成员。
例如:我们可以把上面Yes
的值改成0
,实际上这是不冲突的。因为数字枚举有自增的能力,如果遇到赋予初始值的枚举成员,就会从这个枚举成员重新开始自增:
enum Response {
No = 0,
Yes = 0,
Default
}
// 如果你访问 No 是 0,访问 Yes 也是 0,其实是不能完全保证其唯一性的
console.log(Response.No) // 0
console.log(Response.Yes) // 0
console.log(Response.Default) // 1
针对上面这些问题,那么我们自然而然的想到了另外一种更有针对性的类型:
- 字符串枚举类型
字符串枚举的概念很简单,但是有细微的运行时的差别。在一个字符串枚举里,每个成员都必须用字符串字面量,或另外一个字符串枚举成员进行初始化。
例如我们声明一个字符串枚举:
enum Direction {
Up = "Up",
Down = "Down",
Left = "Left",
Right = "Right"
}
由于字符串枚举没有自增长
行为,字符串枚举可以很好的序列化。换句话说,如果你正在调试并且必须要读一个数字枚举的运行时候的值,这个值通常是很难读的 - 它并不能表达有用的信息
。
但字符串枚举允许你提供一个运行时有意义的
并且可读
的值,独立于枚举成员的名字。
简而言之就是使枚举类型更加富有语义化了。
除此之外,还有异构枚举、联合枚举和const
枚举,这里不再进行赘述。但无一例外,我们都是想让其具备有等级更高的唯一性。甚至于为了唯一性,我们还可以加上Symbol
:
enum Direction {
Up = Symbol('Up'),
Down = Symbol('Down'),
Left = Symbol('Left'),
Right = Symbol('Right')
}
但在日常开发的过程中,我们能百分百确定不会出现下面这种情况吗?
// 首次声明
enum Direction {
Up = "Up",
Down = "Down",
Left = "Left",
Right = "Right"
}
// 团队其他人声明
enum Direction {
North = "Up",
South = "Down",
West = "Left",
East = "Right"
}
// 又或者是
enum Position {
Attitude = Symbol('position'),
Langtitude = Symbol('position')
}
console.log(Direction.Up) // Up
console.log(Direction.North) // Up
console.log(Position.Attitude) // Symbol(position)
console.log(Position.Langtitude) // Symbol(position)
// 当然,这两者是肯定不等的
console.log(Position.Attitude == Position.Langtitude) // false
二、与 Symbol 分析对比
- 唯一性。
Symbol
总是唯一的,如果不刻意共享(Symbol.for()
),但enum
枚举类型不是绝对唯一的,反而会受到其他数值或字符串干扰; - 标识符。
enum
想让枚举更加靠近标识符的概念,有时反而会将数据变得越来越复杂(例如赋值字符串或者Symbol),但又不一定实用。当然这样枚举值也可以作为对象的键值。相反Symbol
就不会有这种烦恼; - 日常开发中经常使用
enum
来规避魔数, 让代码更加整洁和规范, 可读性也较高;而Symbol
只有仅有的一点作用,作为对象属性的标识符
; - 介于前三点的分析,
Symbol
作为标识符的时候,比较单纯一点;而enum
枚举类型用处比较广泛,它是有条件作为对象属性的标识符的。但如果仅仅只是需要标识某个对象,未免有点浪费资源了,并不推荐。
三、最后
结论:我们分析完这两者之后,其实已经发现,enum
的作用远比Symbol
要来的多,同时也具备有标识符的属性。而Symbol
非常的单纯,就好比正在上学的学生,一心向学。前者也会学习,但社会技能往往要多得多了。
其实两者并没有太多的可比性,只能说分场景吧。不同的场景下那种数据类型更加契合业务就用哪种。如果你只需要一个标识符,可以考虑使用Symbol
;如果你是一个相对来讲比较复杂的业务场景,那么可能需要一个可读性强且用来规避魔数的数据类型,那么枚举将是你的不二之选。
以上就是本篇文章的所有内容,都是本菜鸟的个人理解,有哪里不对的欢迎评论区交流指正~
通篇阅读下来,我想要分享的不是如何解决问题,而是面对问题时自己思考问题的思维。我喜欢发散衍生,举一反三,希望大家会喜欢!
下一篇继续学习TypeScript Symbol
类型。