前言

也许前端的同学会问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. 手撸Mysql原生语句--单表

    select from where group by having order by limit 上面的所有操作是有执行的优先级的顺序的,我们将执行的过程可以总结为下面所示的七个步骤. 1.找到表:f ...

  2. ios自动识别电话并变色的问题解决方法

    问题: 在做移动端页面时发现长串数字都被ios系统的手机识别为电话号码,且文字变成很土的蓝色,点击有下划线并弹出提示拨打该电话号码. 解决方法: 1.在head中加上下面这行代码就OK了(仅限于单页面 ...

  3. Nuget管理自己的项目库

    Nuget是什么 Nuget 是一种 Visual Studio 扩展工具,它能够简化在 Visual Studio 项目中添加.更新和删除库(部署为程序包)的操作.(官方地址)相信大家对这个应该还是 ...

  4. Spring Boot 第六弹,拦截器如何配置,看这儿~

    持续原创输出,点击上方蓝字关注我吧 目录 前言 Spring Boot 版本 什么是拦截器? 如何自定义一个拦截器? 如何使其在Spring Boot中生效? 举个栗子 思路 根据什么判断这个接口已经 ...

  5. Java知识系统回顾整理01基础02面向对象03方法

    一.根据实例给出"方法"的定义 在LOL中,一个英雄可以做很多事情,比如超神,超鬼,坑队友 能做什么在类里面就叫做方法 比如队友残血正在逃跑,你过去把路给别人挡住了,导致他被杀掉. ...

  6. Python实现的数据结构与算法之快速排序详解

    一.概述 快速排序(quick sort)是一种分治排序算法.该算法首先 选取 一个划分元素(partition element,有时又称为pivot):接着重排列表将其 划分 为三个部分:left( ...

  7. 全方位剖析 Linux 操作系统,太全了!!!

    Linux 简介 UNIX 是一个交互式系统,用于同时处理多进程和多用户同时在线.为什么要说 UNIX,那是因为 Linux 是由 UNIX 发展而来的,UNIX 是由程序员设计,它的主要服务对象也是 ...

  8. Word云(标签云)生成器控件。net Windows。形式在c#中

    下载demo - 37.1 KB 下载source code - 48.7 KB 背景 这种控制方式的灵感来自于一种名为Wordle的基于网络的免费单词云生成器.实际上,这个控件是我的项目http:/ ...

  9. Apple uses Multipath TCP

    http://blog.multipath-tcp.org/blog/html/2018/12/15/apple_and_multipath_tcp.html December 15, 2018 Ap ...

  10. springboot1.5和jpa利用HikariCP实现多数据源的使用

    背景 现在已有一个完整的项目,需要引入一个新的数据源,其实也就是分一些请求到从库上去 技术栈 springboot1.5 (哎,升不动啊) 思路 两个数据源,其中一个设置为主数据源 两个事物管理器,其 ...