浅析TypeScript中const和readonly的区别、枚举和常量枚举的区别以及关于typescript中枚举的相关知识

一、const 和 readonly 的区别

1、TypeScript 中不可变量的实现方法有两种:

  • 使用 ES6 的 const 关键字声明的值类型
  • 被 readonly 修饰的属性

2、TypeScript 中 readonly:

  TypeScript 中的只读修饰符,可以声明更加严谨的可读属性。通常在 interface 、 Class 、 type 以及 array 和 tuple 类型中使用它,也可以用来定义一个函数的参数。

3、两者区别:

(1)const 用于变量, readonly 用于属性

(2)const 在运行时检查, readonly 在编译时检查

(3)const 声明的变量不得改变值,这意味着,const 一旦声明变量,就必须立即初始化,不能留到以后赋值;

  readonly 修饰的属性能确保自身不能修改属性,但是当你把这个属性交给其它并没有这种保证的使用者(允许出于类型兼容性的原因),他们能改变

const foo: {
  readonly bar: number;
} = {
  bar: 123
};
function iMutateFoo(foo: { bar: number }) {
  foo.bar = 456;
}
iMutateFoo(foo);
console.log(foo.bar); // 456

(4)const 保证的不是变量的值不得改动,而是变量指向的那个内存地址不得改动,例如使用 const 变量保存的数组,可以使用 push , pop 等方法。

  但是如果使用 ReadonlyArray<number> 声明的数组不能使用 push , pop 等方法

二、枚举和常量枚举的区别

1、枚举和常量枚举(const枚举):使用枚举可以清晰地表达意图或创建一组有区别的用例

// 枚举
enum Color { Red, Green, Blue }
// 常量枚举
const enum Color { Red, Green, Blue }

2、区别:

(1)枚举会被编译时会编译成一个对象,可以被当作对象使用

(2)const 枚举会在 typescript 编译期间被删除,const 枚举成员在使用的地方会被内联进来,避免额外的性能开销

  我们先看下面代码,枚举会被编译成什么:

// 枚举
enum Color {
  Red,
  Green,
  Blue
}
var sisterAn = Color.Red
// 会被编译成 JavaScript 中的 var sisterAn = Color.Red
// 即在运行执行时,它将会查找变量 Color 和 Color.Red

  我们再看下 常量枚举 会被编译成什么:

// 常量枚举
const enum Color {
  Red,
  Green,
  Blue
}
var sisterAn = Color.Red
// 会被编译成 JavaScript 中的 var sisterAn = 0
// 在运行时已经没有 Color 变量

  由此可见,使用 常量枚举 会有更好的性能。

  定义的枚举,在经过编译器编译后是一个对象,这个对象我们可以在程序运行时使用,前面有说到。但有时定义枚举可能只是为了让程序可读性更好,而不需要编译后的代码,即不需要编译成对象。typescript中考虑到这种情况,所以加入了 const enum (完全嵌入的枚举)。

三、关于 typescript 中的枚举介绍

1、数字枚举:即枚举里所有属性的值都是数字类型

  当枚举里的属性没指定具体值时,默认值是从 0 开始依次排列,你也可以自己指定具体值,剩下的也是依次递增:

enum Colors {
  Red,
  Blue = 5,
  Yellow
}
console.log(Colors.Red) // 0
console.log(Colors.Blue) // 5
console.log(Colors.Yellow) // 6

  数字枚举在定义值的时候,可以是 常量 或者是 计算出来的。

(1)它是枚举的第一个成员且没有初始化,这种情况下它被赋予值0 (如上)

(2)它未初始化器且它之前的枚举成员是一个 数字常量。 这种情况下,当前枚举成员的值为它上一个枚举成员的值加1(如上)

(3)枚举成员使用 常量枚举表达式初始化

  A的值是被计算出来的。注意注释部分,如果某个属性的值是计算出来的,那么它后面一位的成员必须要初始化值

const getValue = () => {
  return 0
}
enum List {
  A = getValue(),
  B = 2,  // 此处必须要初始化值,不然编译不通过
  C
}
console.log(List.A) // 0
console.log(List.B) // 2
console.log(List.C) // 3

2、反向映射

  可以通过 Enum[key] 或者 Enum.key 的方式获取到对应的值。typescript 还支持反向映射,即可以通过值来获取键,不过反向映射只支持数字枚举。

enum Status {
  Success = 200,
  NotFound = 404,
  Error = 500
}

console.log(Status.Success) // 200
console.log(Status[200]) // Success
console.log(Status[Status.Success]) // Success

3、字符串枚举要求每个字段的值都必须是字符串字面量,或者是另外一个字符串枚举成员

enum Str {
  Str1 = 'this is string one',
  Str2 = 'this is string two',
  Str3 = Str1  // 这里引用了Str1的值
}

4、异构枚举:把数字枚举和字符串枚举混用,就形成了异构枚举,这种方式很容易引起混淆,不推荐使用。

5、枚举成员:枚举成员的值不可修改。

Char.a = 6;  // Cannot assign to 'a' because it is a read-only property

  枚举成员可以分为常量的和需要计算的

(1)const enum(常量枚举):

  1. 没有设置初始值
  2. 对已有枚举成员的引用
  3. 常量的表达式

  常量枚举成员会在编译时计算出结果,然后以常量的形式,出现在运行时环境

(2)computed enum(需要计算的枚举成员

  这些枚举成员的值不会在编译阶段计算,而是保留到程序的执行阶段

enum Char {
  // 常量枚举成员
  a,
  b = Char.a,
  c = 1 + 3,
  // 非常量枚举成员
  d = Math.random(),
  e = 'hello'.length
}

console.log(Char);

6、常量枚举

  常量枚举其实就是是在 enum关键字前使用 const 修饰符,常量枚举会在编译阶段被移除

  作用:当我们不需要一个对象,而需要对象的值,就可以使用常量枚举,这样就可以避免在编译时生成多余的代码和间接引用

const enum Month {
  Jan,
  Feb,
  Mar
}
console.log(Month);  // ReferenceError: Month is not defined 
console.log(Month.Jan); //输出0

  常量枚举成员在使用的地方被内联进来,且常量枚举不可能有计算成员

const enum Directions {
  Up,
  Right,
  Down,
  Left,
  UpRight = 3 + 1,
  RightDown = Directions.Right + 2,
  LeftDown = 9,
  // RightUp = 'hello'.length
// const enum member initializers can only contain literal values and other computed enum values }

 

posted @ 2021-10-15 18:13  古兰精  阅读(2599)  评论(0编辑  收藏  举报