TypeScript魔法堂之枚举的超实用手册


Posted in Javascript onOctober 29, 2020

前言

也许前端的同学会问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官网教程已经对枚举类型进行了详细的讲解说明,我认为最核心是理解清楚其分为两大类:

数字枚举类型

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

特性为:
1.1. 枚举成员附带默认的初始化器;
1.2. 初始化器支持自动增长;
1.3. 支持反向映射。(注意:这里是反向映射,而不是通过值转换为枚举成员)

字符串枚举类型

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呢?

先说说结论:

使用enum的场景:
1.1. 需要使用反向映射时;
1.2. 需要编译后的JavaScript代码保留对象.属性或对象[属性]形式时。

使用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.NO,DialogModes.YES,DialogModes.ALL。于是在.d.ts文件中编写如下外部枚举类型声明

declare enum DialogModes {
  NO,
  YES,
  ALL,
}

总结

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

到此这篇关于TypeScript魔法堂之枚举的超实用手册的文章就介绍到这了,更多相关TypeScript 枚举内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
六款帮助你实现惊艳视差滚动效果的jQuery插件
Sep 14 Javascript
extjs 04_grid 单击事件新发现
Nov 27 Javascript
JavaScript 操作table,可以新增行和列并且隔一行换背景色代码分享
Jul 05 Javascript
js实现的Easy Tabs选项卡用法实例
Sep 06 Javascript
简单讲解jQuery中的子元素过滤选择器
Apr 18 Javascript
noty ? jQuery通知插件全面解析
May 18 Javascript
原生JS实现图片左右轮播
Dec 30 Javascript
Vue + better-scroll 实现移动端字母索引导航功能
May 07 Javascript
PWA介绍及快速上手搭建一个PWA应用的方法
Jan 27 Javascript
Vue 组件参数校验与非props特性的方法
Feb 12 Javascript
JavaScript中的事件与异常捕获详析
Feb 24 Javascript
vue cli3 调用百度翻译API翻译页面的实现示例
Sep 13 Javascript
解决antd的Form组件setFieldsValue的警告问题
Oct 29 #Javascript
vue 函数调用加括号与不加括号的区别
Oct 29 #Javascript
JavaScript实现随机点名小程序
Oct 29 #Javascript
在antd中setFieldsValue和defaultVal的用法
Oct 29 #Javascript
微信小程序淘宝首页双排图片布局排版代码(推荐)
Oct 29 #Javascript
解决antd datepicker 获取时间默认少8个小时的问题
Oct 29 #Javascript
Antd中单个DatePicker限定时间输入范围操作
Oct 29 #Javascript
You might like
BBS(php & mysql)完整版(二)
2006/10/09 PHP
30 个很棒的PHP开源CMS内容管理系统小结
2011/10/14 PHP
js类的静态属性和实例属性的理解
2009/10/01 Javascript
JavaScript高级程序设计(第3版)学习笔记8 js函数(中)
2012/10/11 Javascript
js弹出div并显示遮罩层
2014/02/12 Javascript
JS动态修改图片的URL(src)的方法
2015/04/01 Javascript
JavaScript动态改变div属性的实现方法
2015/07/22 Javascript
js判断radiobuttonlist的选中值显示/隐藏其它模块的实现方法
2016/08/25 Javascript
JavaScript创建对象_动力节点Java学院整理
2017/06/27 Javascript
关于微信小程序登录的那些事
2019/01/08 Javascript
jQuery无冲突模式详解
2019/01/17 jQuery
Vuex的actions属性的具体使用
2019/04/14 Javascript
Vue+axios+WebApi+NPOI导出Excel文件实例方法
2019/06/05 Javascript
vue+element搭建后台小总结 el-dropdown下拉功能
2020/04/10 Javascript
CentOS 8.2服务器上安装最新版Node.js的方法
2020/12/16 Javascript
[01:23:24]DOTA2-DPC中国联赛 正赛 PSG.LGD vs Elephant BO3 第三场 2月7日
2021/03/11 DOTA
python实现rest请求api示例
2014/04/22 Python
Python中str is not callable问题详解及解决办法
2017/02/10 Python
Python实现文件内容批量追加的方法示例
2017/08/29 Python
Python安装lz4-0.10.1遇到的坑
2018/05/20 Python
pytorch 使用单个GPU与多个GPU进行训练与测试的方法
2019/08/19 Python
pymysql模块的使用(增删改查)详解
2019/09/09 Python
python之yield和Generator深入解析
2019/09/18 Python
Pytorch maxpool的ceil_mode用法
2020/02/18 Python
python实现udp传输图片功能
2020/03/20 Python
python文件及目录操作代码汇总
2020/07/08 Python
Python创建临时文件和文件夹
2020/08/05 Python
巴黎卡诗美国官方网站:始于1964年的头发头皮护理专家
2017/07/10 全球购物
美国最大的家庭鞋类零售商之一:Shoe Carnival
2017/10/06 全球购物
Carter’s官方旗舰店:美国受欢迎的婴童服装品牌
2018/01/21 全球购物
laravel使用redis队列实例讲解
2021/03/23 PHP
视光学专业毕业生推荐信
2013/10/28 职场文书
授权委托书(法人单位用)
2014/09/29 职场文书
培训学校2015年度工作总结
2015/07/20 职场文书
最新农村养殖致富:资金投入较低的创业项目有哪些?
2019/09/26 职场文书
Python中OpenCV实现查找轮廓的实例
2021/06/08 Python