前言

也许前端的同学会问JavaScript从诞生至今都没有枚举类型,我们不是都活得挺好的吗?为什么TypeScript需要引入枚举类型呢?

也许被迫写前端的后端同学会问,TypeScript的枚举类型是和Java/.NET的一样吗?

下面我们来一起探讨和尝试解答吧!

前端一直都需要枚举

我敢保证,前端的同学都会万分肯定地告诉大家:我们从来没有写过枚举。那是因为虽然ECMAScript将enum作为保留字,但至ES2020为止还没有提出枚举的实现规范。语言没有提供规范和语言实现,不代表思想活跃勇于造轮子的程序员们不会自己撸一个。

如果语言没有提供,还有那么毅然决然要自己造一个,那枚举到底能解决我们什么问题呢?

枚举真的有点用

首先,枚举字面上的意思就遍历一个存在若干个的值有穷集合的所有成员。核心有两点:

  1. 有穷集合;
  2. 遍历。

也就是说,只要我们需要表示某个变量的值必须为某个有穷集合的成员时,我们是怎么也绕不开枚举的。

写个JavaScript版本的枚举

下面是刚好满足大部分业务需求的枚举实现:

class Color {
// tricky:自增枚举成员值
static counter = null // 枚举成员
static Red = new Color('Red')
static Green = new Color('Green') // 反向映射
static valueOf(value) {
for (var name in Color) {
if (!(name in Color.prototype) && Color[name].value === value) {
return Color[name]
}
}
} constructor(name, value){
if ('counter' in Color);else return this.name = name
if (value == null) {
if (Color.counter === null) {
this.value = Color.counter = 0
}
else {
this.value = ++Color.counter
}
}
else {
this.value = Color.counter = value
}
} toString() {
return `Color.${this.name}`
}
}
delete Color.counter
Object.freeze(Color) // tricky:禁止在定义之外的位置修改枚举成员

其实我们只想表达某些变量将以含有Red、Green两个成员的Color有穷集合作为值域而已,却要写这么多语义无关的代码(严格遵循“能写hi绝对不写hello”原则)。而且在一般规模的项目当中,往往不止一个枚举类型,复制粘贴确实可以解决问题,但真心不优雅。

而TypeScript内置枚举的语言实现恰恰能解决这个问题。

TypeScript的枚举和后端的真不一样

后端的同学对枚举绝对是不会陌生的(除非是Pyton/Nodejs后端的同学啦),虽然TypeScript是JavaScript的超集,但最终需要编译为JavaScript代码,并且要兼容现有JavaScript库,所以确实无法和后端的枚举类型一模一样。

所以我还是建议大家运用空杯心理,重头理解TypeScript的枚举类型,将过去的知识作为助燃剂,而不是围栏更适宜。

数字枚举类型和字符串枚举类型

TypeScript官网教程已经对枚举类型进行了详细的讲解说明,我认为最核心是理解清楚其分为两大类:

  1. 数字枚举类型
enum Response {
No = 0, // 手动设置初始化器
Yes = // 附加默认的支持自动增长的初始化器,因此Yes的值为1
}

特性为:

1.1. 枚举成员附带默认的初始化器;

1.2. 初始化器支持自动增长;

1.3. 支持反向映射。(注意:这里是反向映射,而不是通过值转换为枚举成员)

  1. 字符串枚举类型
enum Color {
Red = 'Red',
Green = 'Green',
}

特性为:

1.1. 必须为枚举成员设置初始化器;

1.2. 初始化器不支持自动增长;

1.3. 不支持反向映射。

而计算和常量成员其实就是上述两种枚举类型中初始化器的细分特性罢了。

enum让数字枚举类型反向映射成为可能

上一节介绍到数字枚举类型支持反向映射,但前提是通过enum定义的数字枚举类型才支持。那是因为enum Respose {No,Yes,}最终会被编译为如下JavaScript代码:

var Response;
(function (Response) {
Response[Response["No"] = 0] = "No";
Response[Response["Yes"] = 1] = "Yes";
})(Response || (Response = {}));

那么我们就可以通过Response[0]反向映射得到"No"

但对于字符串枚举类型就没有这种好事了,看看enum Color {Red='Red',Green='Green',}编译出来的代码吧

var Color;
(function (Color) {
Color["Red"] = "Red";
Color["Green"] = "Green";
})(Color || (Color = {}));

只能说非常朴实无华,就这样没啥好说的,大家在使用时注意差异就好了。

const enum高效的编译时内联

官方文档明确写出“大多数情况下,枚举是十分有效的方案。 然而在某些情况下需求很严格。 为了避免在额外生成的代码上的开销和额外的非直接的对枚举成员的访问,我们可以使用 const枚举”,那是为什么呢?

那是因为通过const enum定义的编译时枚举类型,效果和通过C/C++的#define定义常量没实质区别。说白了就是假如仅仅通过通过const enum定义了枚举类型而没有其它地方调用,这段代码将在编译时被直接抹掉。

当其它地方调用该枚举类型时,将直接把枚举类型成员的值内联到使用处,如下:

const enum Response {
No,
Yes,
} console.log(Response.NO, Response.Yes)

编译后成为console.log(0, 1),运行效果自然只能比enum定义的好。

什么时候用enum?又在什么场景下用const enum呢?

先说说结论:

  1. 使用enum的场景:

    1.1. 需要使用反向映射时;

    1.2. 需要编译后的JavaScript代码保留对象.属性对象[属性]形式时。
  2. 使用const enum的场景:能不用enum时就用const enum(哈哈!)

使用enum的场景中的第一条还很好理解,但第二条是啥回事呢?我这里有个真实发生的示例,可以让大家更好的理解:

背景:为Photoshop的ExtendScript编写类型声明。

需求:DialogModes.NO在ExtendScript中返回值为DialogModes.No本身,编译后的JavaScript中必须保留DialogModes.NO的代码形式。

那么又为何鼓励大家能用const enum时就用const enum呢?

这是TypeScript为大家特意准备的编译时优化方式,好东西为啥不用呢?编译时优化难道不香吗?

外部枚举declare enum的作用?

所谓外部枚举,即使我们为了在TypeScript开发环境下,更好地使用某些已采用JavaScript编写的库,而被迫为其编写的枚举类型声明。

如ExtendScript标准库存在枚举DialogModes.NODialogModes.YESDialogModes.ALL。于是在.d.ts文件中编写如下外部枚举类型声明

declare enum DialogModes {
NO,
YES,
ALL,
}

总结

对于日常开发中我们绕不过枚举类型,TypeScript为我们提供语言实现和编译时优化,除了保护了我们为如何优化实现枚举类型而日思夜想导致日渐稀疏的头发外,还大大降低了因复制粘贴带来的代码库体积徒增的风险。

写更少的代码,做正确的事,早点下班岂不更快哉_

转载请注明来自:https://www.cnblogs.com/fsjohnhuang/p/13893641.html —— _肥仔John

TypeScript魔法堂:枚举的超实用手册的更多相关文章

  1. TypeScript魔法堂:函数类型声明其实很复杂

    前言 江湖有传"动态类型一时爽,代码重构火葬场",由于动态类型语言在开发时不受数据类型的约束,因此非常适合在项目原型阶段和初期进行快速迭代开发使用,这意味着项目未来将通过重写而非重 ...

  2. JS魔法堂:不完全国际化&本地化手册 之 理論篇

    前言  最近加入到新项目组负责前端技术预研和选型,其中涉及到一个熟悉又陌生的需求--国际化&本地化.熟悉的是之前的项目也玩过,陌生的是之前的实现仅仅停留在"有"的阶段而已. ...

  3. JS魔法堂:不完全国际化&本地化手册 之 实战篇

    前言  最近加入到新项目组负责前端技术预研和选型,其中涉及到一个熟悉又陌生的需求--国际化&本地化.熟悉的是之前的项目也玩过,陌生的是之前的实现仅仅停留在"有"的阶段而已. ...

  4. HTML5魔法堂:全面理解Drag & Drop API

    一.前言    在HTML4的时代,各前端工程师为了实现拖拽功能可说是煞费苦心,初听HTML5的DnD API觉得那些痛苦的日子将一去不复返,但事实又是怎样的呢?下面我们一起来看看DnD API的真面 ...

  5. WebComponent魔法堂:深究Custom Element 之 标准构建

    前言  通过<WebComponent魔法堂:深究Custom Element 之 面向痛点编程>,我们明白到其实Custom Element并不是什么新东西,我们甚至可以在IE5.5上定 ...

  6. WebComponent魔法堂:深究Custom Element 之 面向痛点编程

    前言  最近加入到新项目组负责前端技术预研和选型,一直偏向于以Polymer为代表的WebComponent技术线,于是查阅各类资料想说服老大向这方面靠,最后得到的结果是:"资料99%是英语 ...

  7. CSS魔法堂:Box-Shadow没那么简单啦:)

    前言  说起box-shadow那第一个想法当然就是用来实现阴影,其实它还能用于实现其他好玩的效果的,本篇就打算说说box-shadow的那些事. 二话不说看效果 3D小球 <style typ ...

  8. CSS魔法堂:重拾Border之——更广阔的遐想

    前言  当CSS3推出border-radius属性时我们是那么欣喜若狂啊,一想到终于不用再添加额外元素来模拟圆角了,但发现border-radius还分水平半径和垂直半径,然后又发现border-t ...

  9. CSS魔法堂:重拾Border之——不仅仅是圆角

    前言  当CSS3推出border-radius属性时我们是那么欣喜若狂啊,一想到终于不用再添加额外元素来模拟圆角了,但发现border-radius还分水平半径和垂直半径,然后又发现border-t ...

随机推荐

  1. UOS服务器常见问题

    UOS服务器安装完成之后,xshell工具无法远程, 描述 我安装完系统后,用xshell工具无法远程,必须在系统上才能进去我是通过centos7的kvm虚拟工具上刷的统信的系统,安装方式: 最小化安 ...

  2. java调用matlab生成exe文件

    一.Matlab生成Java Package 1.在MATLAB的Command Window输入deploytool命令,选择Library Compiler. 2.在弹出的窗口选择Java Pac ...

  3. Apache Shiro 1.3.2入门

    简介 Apache Shiro是一个功能强大且灵活的开放源代码安全框架,可以清楚地处理认证,授权,企业会话管理和加密.Apache Shiro的首要目标是易于使用和理解.有时候安全性可能非常复杂和痛苦 ...

  4. Centos-获取远程主机对应端口信息-telnet

    telnet 通过 telnet协议与远程主机通信或者获取远程主机对应端口信息 格式 telnet URL/IP port

  5. Java知识系统回顾整理01基础03变量06变量的作用域

    一.变量根据所处的位置对应不同的名称 变量处于不同的位置,有不同的名称 名称分别是  字段,属性 参数 局部变量 不同名称的变量,其作用域是不一样的 二.字段,属性,Field 当一个变量被声明在类下 ...

  6. Springboot集成logback,控制台日志打印两次,并且是不同的线程打印的

    背景 在搭建一个新项目的时候,从公司别的项目搞了个logback-spring.xml的配置过来,修改一下启动项目的时候发现 所有的日志都输出了两次 并且来自于不同的线程,猜测是配置重复了,但是仔细检 ...

  7. JVM性能调优(3) —— 内存分配和垃圾回收调优

    前序文章: JVM性能调优(1) -- JVM内存模型和类加载运行机制 JVM性能调优(2) -- 垃圾回收器和回收策略 一.内存调优的目标 新生代的垃圾回收是比较简单的,Eden区满了无法分配新对象 ...

  8. C++调用全局函数与类成员函数

    void testfunc(void *param) { printf("\n\tcall global function %s\n", param); } void *GetCl ...

  9. JS学习之路一

    1.准备 ①安装vscode 地址:https://vscode.en.softonic.com/ ②安装node.js node -v npm -v 地址:https://nodejs.org/zh ...

  10. C#使用RabbitMq队列(Sample,Work,Fanout,Direct等模式的简单使用)

    1:RabbitMQ是个啥?(专业术语参考自网络) RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件). RabbitMQ服务器是用Erlang语言编写的, ...