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 相关文章推荐
B/S开发中常用javaScript技术与代码
Mar 09 Javascript
对YUI扩展的Gird组件 Part-1
Mar 10 Javascript
jQuery动画出现连续触发、滞后反复执行的解决方法
Jan 28 Javascript
jQuery实现长按按钮触发事件的方法
Feb 02 Javascript
JavaScript 面向对象与原型
Apr 10 Javascript
原生js实现秒表计时器功能
Feb 16 Javascript
提高JavaScript执行效率的23个实用技巧
Mar 01 Javascript
SelectPage v2.4 发布新增纯下拉列表和关闭分页功能
Sep 07 Javascript
详解html-webpack-plugin用法全解
Jan 22 Javascript
ios设备中angularjs无法改变页面title的解决方法
Sep 13 Javascript
详解小程序如何避免多次点击,重复触发事件
Apr 08 Javascript
Vue+scss白天和夜间模式切换功能的实现方法
Jan 05 Vue.js
解决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
基于PHP选项与信息函数的使用详解
2013/05/10 PHP
php用户登录之cookie信息安全分析
2016/05/13 PHP
Laravel向公共模板赋值方法总结
2019/06/25 PHP
北京奥运官方网站幻灯切换效果flash版打包下载
2008/01/30 Javascript
javascript addBookmark 加入收藏 多浏览器兼容
2009/08/15 Javascript
JQuery 构建客户/服务分离的链接模型中Table分页代码效率初探
2010/01/22 Javascript
JS 进度条效果实现代码整理
2011/05/21 Javascript
基于MVC3方式实现下拉列表联动(JQuery)
2013/09/02 Javascript
JavaScript跨浏览器获取页面中相同class节点的方法
2015/03/03 Javascript
详解JS中Array对象扩展与String对象扩展
2016/01/07 Javascript
angularjs封装bootstrap时间插件datetimepicker
2016/06/20 Javascript
Node.js的环境安装配置(使用nvm方式)
2016/10/11 Javascript
js querySelector() 使用方法
2016/12/21 Javascript
react.js 父子组件数据绑定实时通讯的示例代码
2017/09/25 Javascript
通过实例了解js函数中参数的传递
2019/06/15 Javascript
Element Badge标记的使用方法
2020/07/27 Javascript
JavaScript 获取滚动条位置并将页面滑动到锚点
2021/02/08 Javascript
python爬虫框架scrapy实战之爬取京东商城进阶篇
2017/04/24 Python
Python实现将一个正整数分解质因数的方法分析
2017/12/14 Python
Python3实现带附件的定时发送邮件功能
2020/12/22 Python
python实现自主查询实时天气
2018/06/22 Python
Django渲染Markdown文章目录的方法示例
2019/01/02 Python
Python利用sqlacodegen自动生成ORM实体类示例
2019/06/04 Python
python3 tkinter实现点击一个按钮跳出另一个窗口的方法
2019/06/13 Python
Django 大文件下载实现过程解析
2019/08/01 Python
python字典排序的方法
2019/10/12 Python
python tkinter canvas使用实例
2019/11/04 Python
Python小程序之在图片上加入数字的代码
2019/11/26 Python
django中related_name的用法说明
2020/05/20 Python
HTML5新增的表单元素和属性实例解析
2014/07/07 HTML / CSS
基于HTML5的WebGL实现json和echarts图表展现在同一个界面
2017/10/26 HTML / CSS
eVitamins日本:在线购买折扣维生素、补品和草药
2019/04/04 全球购物
Paper Cape官网:美国婴儿和儿童服装品牌
2019/11/02 全球购物
考生诚信考试承诺书
2014/05/23 职场文书
企业群众路线教育实践活动心得体会
2014/11/03 职场文书
MySQL infobright的安装步骤
2021/04/07 MySQL