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 相关文章推荐
javascript中关于break,continue的特殊用法与介绍
May 24 Javascript
jQuery的deferred对象详解
Nov 12 Javascript
JavaScript将字符串转换成字符编码列表的方法
Mar 19 Javascript
JQuery遍历DOM节点的方法
Jun 11 Javascript
微信小程序 HTTPS报错整理常见问题及解决方案
Dec 14 Javascript
js实现年月日表单三级联动
Apr 17 Javascript
微信小程序访问node.js接口服务器搭建教程
Apr 25 Javascript
使用D3.js制作图表详解
Aug 13 Javascript
mongoose设置unique不生效问题的解决及如何移除unique的限制
Nov 07 Javascript
微信小程序整合使用富文本编辑器的方法详解
Apr 25 Javascript
用node.js写一个jenkins发版脚本
May 21 Javascript
JS sort方法基于数组对象属性值排序
Jul 10 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
php INI配置文件的解析实现分析
2011/01/04 PHP
PHP 命令行工具 shell_exec, exec, passthru, system详细使用介绍
2011/09/11 PHP
php 获取今日、昨日、上周、本月的起始时间戳和结束时间戳的方法
2013/09/28 PHP
php获取服务器端mac和客户端mac的地址支持WIN/LINUX
2014/05/15 PHP
PHP获取Exif缩略图的方法
2015/07/13 PHP
PHP实现通过文本文件统计页面访问量功能示例
2019/02/13 PHP
关于在IE下的一个安全BUG --可用于跟踪用户的系统鼠标位置
2013/04/17 Javascript
JSONP获取Twitter和Facebook文章数的具体步骤
2014/02/24 Javascript
jquery实现简单的轮换出现效果实例
2015/07/23 Javascript
基于JS实现移动端访问PC端页面时跳转到对应的移动端网页
2020/12/24 Javascript
BootStrap 智能表单实战系列(十)自动完成组件的支持
2016/06/13 Javascript
JS采用绝对定位实现回到顶部效果完整实例
2016/06/20 Javascript
实用jquery操作表单元素的简单代码
2016/07/04 Javascript
微信小程序 picker 组件详解及简单实例
2017/01/10 Javascript
Javascript中引用类型传递的知识点小结
2017/03/06 Javascript
jQuery zTree 异步加载添加子节点重复问题
2017/11/29 jQuery
实例解析Vue.js下载方式及基本概念
2018/05/11 Javascript
[01:08:09]DOTA2上海特级锦标赛主赛事日 - 1 胜者组第一轮#1Liquid VS Alliance第二局
2016/03/02 DOTA
Python3调用微信企业号API发送文本消息代码示例
2017/11/10 Python
python 找出list中最大或者最小几个数的索引方法
2018/10/30 Python
在python中利用numpy求解多项式以及多项式拟合的方法
2019/07/03 Python
Python values()与itervalues()的用法详解
2019/11/27 Python
python退出循环的方法
2020/06/18 Python
Python容器类型公共方法总结
2020/08/19 Python
python+flask编写一个简单的登录接口
2020/11/13 Python
pycharm 2020.2.4 pip install Flask 报错 Error:Non-zero exit code的问题
2020/12/04 Python
python 获取计算机的网卡信息
2021/02/18 Python
Timberland澳大利亚官网:全球领先的户外品牌
2019/12/10 全球购物
Simons官方网站:加拿大时尚零售商
2020/02/20 全球购物
UNIX文件名称有什么规定
2013/03/25 面试题
《中彩那天》教学反思
2014/02/22 职场文书
志愿者服务活动总结报告
2015/05/06 职场文书
辩论赛开场白大全(主持人+辩手)
2015/05/29 职场文书
学校扫黄打非工作总结
2015/10/15 职场文书
公证书
2019/04/17 职场文书
MySQL慢查询优化解决问题
2022/03/17 MySQL