CSDN话题挑战赛第2期
参赛话题:学习笔记
# 扩展类型
`基础类型`已经适用于绝大数场景,但是`扩展类型`可以持续优化我们的代码,使得我们效率更高
具体分为:
- **类型别名**
- **枚举**
- **接口**
- **类**
# 枚举
## 定义
枚举通常`约束`某个变量,某个函数返回值等的`取值范围`.
**其实字面量和联合类型配合使用, 也可以达到同样的目的, 这样的话要枚举做啥呢??**
## 字面量类型的问题
> ①在类型约束位置,会产生重复代码。**可以使用类型别名解决这个问题**
```
let gender: '男' | '女';
gender = "女"
// 这个时候重复定义,产生重复代码
function searchGender(g: '男' | '女') {}
---
// 利用类型别名解决重复
type Gender = '男' | '女';
let gender: Gender
gender = "女"
function searchGender(g: Gender) {}
```
> ②定义的联合类型的数值。和使用真实的值不一样时,**会产生大量的修改**
```
// 项目经理要求改变类型,觉得男|女太俗了
type Gender = '李易峰' | '美女';
let gender: Gender
// 这个时候如果代码有几千行都赋值原来的定义,那要累死个人
gender = "女"
gender = "男"
function searchGender(g: Gender) {}
```
> ③字面量类型不会进入编译结果
```
// 编译成js后消失, 并且无法在ts中拿到所有定义的值去渲染或循环啥的
type Gender = '李易峰' | '美女';
```
**如果项目中遇到后两个问题,那只能靠`枚举大哥`来解决啦**
## 枚举的使用
- **使用格式**
```
enum 枚举名 {
枚举字段1 = 枚举值1,
枚举字段2 = 枚举值2
}
```
**解决第二个问题**
```
enum Gender {
male = '李易峰',
female = '美女'
}
// 保持不变
let gender: Gender
gender = Gender.female
gender = Gender.male
console.log(Gender.female); // 美女
// 保持不变
function searchGender(g: Gender) {}
```
- **解决第三个问题**,**枚举**会出现在**编译结果**中,编译结果中表现为**对象**
```
/**
* 这块内容相当于转化为一个对象
* {
* male: "\u674E\u6613\u5CF0",
* female: "\u7F8E\u5973"
* }
*/
var Gender;
(function (Gender) {
// 中文被转化为unicode
Gender["male"] = "\u674E\u6613\u5CF0";
Gender["female"] = "\u7F8E\u5973";
})(Gender || (Gender = {}));
let gender;
gender = Gender.female;
gender = Gender.male;
console.log(Gender.female);
function searchGender(g) { }
```
并且我们可以在`ts`代码里直接打印枚举, 就把他认为是一个`对象`就完事了
```
function print () {
const vals = Object.values(Gender)
vals.forEach(v=>console.log(v)) // 李易峰 美女
}
```
所以我们在做取值范围的时候,**用枚举准没错**
## 枚举细节玩法
1. 枚举的字段值可以是**字符串或数字**
```
enum Gender {
male = [], // 报错: “含字符串值成员的枚举中不允许使用计算值。”
female = '美女'
}
```
2. 数字枚举的值会**自动递增**, 每个**增加1**。 如果第一位不写,默认从0开始递增
```
enum level {
level1= 1,
level2,
level3,
}
console.log(level2) //2
console.log(level3) //3
---
enum level {
level1,
level2,
level3,
}
console.log(level1) //0
console.log(level2) //1
console.log(level3) //2
```
3. 被**数字枚举**约束的变量,可以**直接赋值为数字**,
`这会导致一系列的问题,最好不要这么做`
```
enum level {
level1= 1,
level2,
level3,
}
let a:level = 2
```
4. `数字枚举`的编译结果 和 `字符串的枚举`编译结果 `有差异`
```
数字枚举便编译结果
var level;
(function (level) {
level[level["level1"] = 1] = "level1";
level[level["level2"] = 2] = "level2";
level[level["level3"] = 3] = "level3";
})(level || (level = {}));
```
```
ts源码
enum level {
level1= 1,
level2,
level3,
}
console.log(level)
{
'1': 'level1',
'2': 'level2',
'3': 'level3',
level1: 1,
level2: 2,
level3: 3
}
```
## 枚举的最佳实践
- `尽量不要在枚举中即出现字符串,又出现数字`,这个会引发很多问题的出现
- 使用枚举值,`尽量都使用枚举的字段名`
---
# 枚举的扩展-位运算
## 什么是位运算?
`是把两个数字换算成二进制后对每一位进行运算`
包含这`3`种形式:
- **或**运算: 对每一位去`||`, 比较两者有一个为1就为1, 否者为0
```
let a = 1 | 8 //=>9, => 1001
// 推理过程如下:
0001
1000
1(0||1) 0(0||0) 0(0||0) 1(1||0)
```
- **且**运算: 对每一位去`&&`, 比较两者都为1才为1, 否者为0
```
let a = 1 & 8 //=>0, => 0000
// 推理过程如下:
0001
1000
1(0&&1) 0(0&&0) 0(0&&0) 1(1&&0)
```
- **异或**运算: 对每一位去比较`异同`, 如果相同=0,若果不同=1
```
let a = 1 ^ 8 //=>9, => 1001
// 推理过程如下:
0001
1000
1(不同) 0(相同) 0(相同) 1(不同)
```
---
## 枚举中使用位运算
> 位枚举,也称枚举的位运算, 主要是**针对数字类型的枚举**
我们如何理解呢, 举个例子:
现在有个需求: 需要完成**读**,**写**,**创建**,**删除**,四种权限的组合, 我们如何取用枚举实现呢?
- 第一步-`创建枚举`
```
// 创建一个0-3的数字枚举, 包含读,写,创建,删除
enum Permission {
Read,
Write,
Create,
Delete
}
```
- 第二步-`设置值`
```
// 利用2进制数值,去表示组合
enum Permission {
Read = 1, //=> 2^0, => 0001
Write = 2, //=> 2^1, => 0010
Create = 4, //=> 2^2, => 0100
Delete = 8 //=> 2^3, => 1000
}
```
- 第三步-`组合使用`
利用或运算,`组合`设置权限:
```
// 设置可读,可写
// 注意这里是赋值, 不是定义类型
// 定义为数字枚举, 可以赋值任意数字
let p: Permission = Permission.Read | Permission.Write
let p: Permission = Permission.Read | Permission..Write | Permission.Delete
```
利用且运算,判断是否`包含`某个权限:
```
let p: Permission = Permission.Read | Permission.Write
function hasPermission(target: Permission, per: Permission) {
return (target & per) === per
}
// 判断p有木有可写权限
hasPermission(p, Permission.Write) // => true
```
利用且运算,`删除`某个权限:
```
let p: Permission = Permission.Read | Permission.Write
p = p ^ Permission.Write //=> p=1
hasPermission(p, Permission.Write) // => false
```