前言
- 近来花了大量时间在做项目重构忽略了ts的学习,今天已经完成了工作
- 接着之前的学习,继续学习我们的ts,今天学枚举
介绍
- 使用枚举我们可以定义一些带名字的常量。 使用枚举可以清晰地表达意图或创建一组有区别的用例。 TypeScript支持数字的和基于字符串的枚举。
枚举
数字枚举
- 我们定义了一个数字枚举, Up使用初始化为 1。 其余的成员会从 1开始自动增长。 换句话说, Direction.Up的值为 1, Down为 2, Left为 3, Right为 4。
- 如果不定义1 那就从0开始递增
1
2
3
4
5
6
7
8
9
10
11
12enum Direction {
Up = 1,
Down,
Left,
Right
}
enum Direction {
Up,//0
Down,//1
Left,//2
}
- 简单的通过枚举的属性来访问枚举成员
- 可能会有一些报错提醒,直接运行node跑js就好了
1
2
3
4
5
6
7
8
9
10enum Response {
No = 0,
Yes = 1,
}
function respond(recipient: string, message: Response): void {
console.log('Yes', message) // 输出 Yes 1
}
respond("Princess Caroline", Response.Yes)
- 数字枚举可以被混入到 计算过的和常量成员(如下所示)。 简短地说,不带初始化器的枚举或者被放在第一的位置,或者被放在使用了数字常量或其它常量初始化了的枚举后面。 换句话说,下面的情况是不被允许的:
- 就是说可以用但是不能放在前面
1
2
3
4enum E {
A = getSomeValue(),
B, // error! 'A' is not constant-initialized, so 'B' needs an initializer
}
字符串枚举
- 字符串枚举的概念很简单,但是有细微的 运行时的差别。 在一个字符串枚举里,每个成员都必须用字符串字面量,或另外一个字符串枚举成员进行初始化。
- 由于字符串枚举没有自增长的行为,字符串枚举可以很好的序列化。 换句话说,如果你正在调试并且必须要读一个数字枚举的运行时的值,这个值通常是很难读的 - 它并不能表达有用的信息(尽管 反向映射会有所帮助),字符串枚举允许你提供一个运行时有意义的并且可读的值,独立于枚举成员的名字。
1
2
3
4
5
6enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
异构枚举
- 从技术的角度来说,枚举可以混合字符串和数字成员,但是似乎你并不会这么做:
- 除非你真的想要利用JavaScript运行时的行为,否则我们不建议这样做。
1
2
3
4enum BooleanLikeHeterogeneousEnum {
No = 0,
Yes = "YES",
}
计算的和常量成员
- 每个枚举成员都带有一个值,它可以是 常量或 计算出来的。 当满足如下条件时,枚举成员被当作是常量:
1
2
3
4
5
6
7
8
9// 默认为数字常量 0
enum E { X }
// 默认为数字常量 当前是上一位的+1 , 0,1,2
enum E1 { X, Y, Z }
enum E2 { // 1,2,3
A = 1, B, C
}
枚举成员使用 常量枚举表达式初始化。 常数枚举表达式是TypeScript表达式的子集,它可以在编译阶段求值。 当一个表达式满足下面条件之一时,它就是一个常量枚举表达式:
- 一个枚举表达式字面量(主要是字符串字面量或数字字面量)
- 一个对之前定义的常量枚举成员的引用(可以是在不同的枚举类型中定义的)
- 带括号的常量枚举表达式
- 一元运算符 +, -, ~其中之一应用在了常量枚举表达式
常量枚举表达式做为二元运算符 +, -, *, /, %, <<, >>, >>>, &, |, ^的操作对象。 若常数枚举表达式求值后为 NaN或 Infinity,则会在编译阶段报错。
运算符 (字符串无法使用)
- | 表示两边求并集(元素相加,相同元素只出现一次)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// 数字例子
enum Test {
A = 1,
B = 1,
C = A | B
}
// 由于两个相等所以只会保留两个
// C = 1 = A = B
enum Test {
A = 1,
B = 2,
C = A | B
}
// C = 3 = A+B
// 字符串例子 x
enum Test {
A = "abc",
B = "bcd",
C = A | B
}
// 这样是错误的 字符串是不允许用计算属性的
1 | enum AnimalFlags { |
- 其实可以理解为前面的数乘以后面的数的2次方倍
- 我们使用了左移的位运算符,将数字 1 的二进制向左移动位置得到数字 0001、0010、0100 和 1000(换成十进制结果是:1, 2, 4, 8)。当你在使用这种标记的时候,这些位运算符 | (或)、& (和)、~ (非)将会是你最好的朋友
- 其余的用个例子来说
- &:表示两边是否其中一个是另外一个的子集,如果是返回子集,否则返回0(如果其中一个包含另外一个,返回被包含的,否则返回0)
- ^:表示从两者的并集中去除两者的交集(把两个的元素合并到一起,如果两个中有公共元素,要将这个公共元素从合并的结果中去除)
- ~:表示取反,返回的结果我还不知道应该是什么,以后再查一下。用法主要和“&”一起使用,例如:去除其中的某个元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50// 枚举
enum AnimalFlags {
None = 0,
HasClaws = 1 << 0,// 1
CanFly = 1 << 1 // 2
}
// 接口
interface Animal {
flags: AnimalFlags; // {None:0,HasClaws:1,CanFly:2}
[key: string]: any;
}
function printAnimalAbilities(animal: Animal) {
console.log('+',animal)
var animalFlags = animal.flags;
if (animalFlags & AnimalFlags.HasClaws) {
console.log('animal has claws');
}
if (animalFlags & AnimalFlags.CanFly) {
console.log('animal can fly');
}
if (animalFlags == AnimalFlags.None) {
console.log('nothing');
}
}
// 赋值
var animal = { flags: AnimalFlags.None };// 0
printAnimalAbilities(animal); // nothing
// 两个相加,相同的不加
animal.flags |= AnimalFlags.HasClaws; // 1
printAnimalAbilities(animal); // animal has claws
// 清除
animal.flags &= ~AnimalFlags.HasClaws; //0
printAnimalAbilities(animal); // nothing
// 和 = 两个的和
animal.flags |= AnimalFlags.HasClaws | AnimalFlags.CanFly; // 3
printAnimalAbilities(animal); // animal has claws, animal can fly
// 结果
+ { flags: 0 }
nothing1
+ { flags: 1 }
animal has claws
+ { flags: 0 }
nothing1
+ { flags: 3 }
animal has claws
animal can fly
联合枚举与枚举成员的类型
- 存在一种特殊的非计算的常量枚举成员的子集:字面量枚举成员。 字面量枚举成员是指不带有初始值的常量枚举成员,或者是值被初始化为
- 任何字符串字面量(例如: “foo”, “bar”, “baz”)
- 任何数字字面量(例如: 1, 100)
- 应用了一元 -符号的数字字面量(例如: -1, -100)
首先,枚举成员成为了类型! 例如,我们可以说某些成员 只能是枚举成员的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29enum ShapeKind {
Circle,
Square,
}
interface Circle {
kind: ShapeKind.Circle;
radius: number;
}
interface Square {
kind: ShapeKind.Square;
sideLength: number;
}
let c: Circle = {
kind: ShapeKind.Square,
// ~~~~~~~~~~~~~~~~ Error!
radius: 100,
}
// 改
let c: Circle = {
kind: 100, // 默认数字类型写个数字
// ~~~~~~~~~~~~~~~~ Error!
radius: 100,
}
console.log(c)
// { kind: 100, radius: 100 }另一个变化是枚举类型本身变成了每个枚举成员的 联合。 虽然我们还没有讨论联合类型,但你只要知道通过联合枚举,类型系统能够利用这样一个事实,它可以知道枚举里的值的集合。 因此,TypeScript能够捕获在比较值的时候犯的愚蠢的错误。 例如:
1
2
3
4
5
6
7
8
9
10
11
12enum E {
Foo,
Bar,
}
function f(x: E) {
if (x !== E.Foo || x !== E.Bar) { // 其实就是说第一个都没有了还判断第二个干嘛是不是傻
// ~~~~~~~~~~~
// Error! Operator '!==' cannot be applied to types 'E.Foo' and 'E.Bar'.
}
}这个例子里,我们先检查 x是否不是 E.Foo。 如果通过了这个检查,然后 ||会发生短路效果, if语句体里的内容会被执行。 然而,这个检查没有通过,那么 x则 只能为 E.Foo,因此没理由再去检查它是否为 E.Bar。
运行时的枚举
- 枚举是在运行时真正存在的对象。 例如下面的枚举:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15enum E {
X, Y, Z
}
function f(obj: { X: number }) {
return obj.X;
}
// 改
function f(obj: { Y: number }) {
return obj.Y;
}
// Works, since 'E' has a property named 'X' which is a number.
console.log(f(E));
// 0
// 1 改后
反向映射
除了创建一个以属性名做为对象成员的对象之外,数字枚举成员还具有了 反向映射,从枚举值到枚举名字。 例如,在下面的例子中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14// 1
enum Enum {
A
}
let a = Enum.A;
let nameOfA = Enum[a]; // "A"
// 2
var Enum;
(function (Enum) {
Enum[Enum["A"] = 0] = "A";
})(Enum || (Enum = {}));
var a = Enum.A;
var nameOfA = Enum[a]; // "A"生成的代码中,枚举类型被编译成一个对象,它包含了正向映射( name -> value)和反向映射( value -> name)。 引用枚举成员总会生成为对属性访问并且永远也不会内联代码。
const枚举
- 大多数情况下,枚举是十分有效的方案。 然而在某些情况下需求很严格。 为了避免在额外生成的代码上的开销和额外的非直接的对枚举成员的访问,我们可以使用 const枚举。 常量枚举通过在枚举上使用 const修饰符来定义。
- 常量枚举只能使用常量枚举表达式,并且不同于常规的枚举,它们在编译阶段会被删除。 常量枚举成员在使用的地方会被内联进来。 之所以可以这么做是因为,常量枚举不允许包含计算成员。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15const enum Enum {
A = 1,
B = A * 2
}
const enum Directions {
Up,
Down,
Left,
Right
}
let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right]
var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];
外部枚举
外部枚举用来描述已经存在的枚举类型的形状。
1
2
3
4
5declare enum Enum {
A = 1,
B,
C = 2
}外部枚举和非外部枚举之间有一个重要的区别,在正常的枚举里,没有初始化方法的成员被当成常数成员。 对于非常数的外部枚举而言,没有初始化方法时被当做需要经过计算的。
后记
- 时隔多日我又回来了,这个就是我学习Ts的第七天的笔记,欢迎更多的同行大哥指导交流
- 欢迎进入我的博客:https://yhf7.github.io/
- 如果有什么侵权的话,请及时添加小编微信以及qq也可以来告诉小编(905477376微信qq通用),谢谢!