函数

函数标注

在这里插入图片描述
如果用 type 或者是 接口 interface 的话可以这样。

在这里插入图片描述

可选参数

在这里插入图片描述

参数的默认值

ES6 的写法类似
在这里插入图片描述
如果需要限制参数的值得话,可以用联合类型

在这里插入图片描述

剩余参数

我们想要合并数组,调用的方法如下图的 merge 方法所示,

那么这个函数的定义就是这样的:

在这里插入图片描述

函数中的 this

普通函数的 this

对于普通函数而言,this 是会随着调用环境的变化而变化的,所以默认情况下,普通函数中的 this 被标注为 any,但我们可以在函数的第一个参数位(它不占据实际参数位置)上显式的标注 this 的类型

在这里插入图片描述

箭头函数的 this

在这里插入图片描述
在这里插入图片描述

函数重载

我们先来看一个问题
在这里插入图片描述

联合类型的话不存在这样的约束,如果想要添加这样的规则,可以通过函数重载来解决:

注意:同名函数并不是覆盖,而是重载

在这里插入图片描述


面向对象编程

面向对象编程中一个重要的核心就是:,当我们使用面向对象的方式进行编程的时候,通常会首先去分析具体要实现的功能,把特性相似的抽象成一个一个的类,然后通过这些类实例化出来的具体对象来完成具体业务需求。

类的基础

在类的基础中,包含下面几个核心的知识点,也是 TypeScriptEMCAScript2015+ 在类方面共有的一些特性

  • class 关键字
  • 构造函数:constructor
  • 成员属性定义
  • 成员方法
  • this 关键字

除了以上的共同特性以外,在 TypeScript 中还有许多 ECMAScript 没有的,或当前还不支持的一些特性,如:抽象

class 关键字

通过 class 就可以描述和组织一个类的结构
在这里插入图片描述

构造函数

通过 class 定义了一个类以后,我们可以通过 new 关键字来调用该类从而得到该类型的一个具体对象:也就是实例化。

为什么类可以像函数一样去调用呢,其实我们执行的并不是这个类,而是类中包含的一个特殊函数:构造函数 - constructor

1
2
3
4
5
6
class User {
constructor() {
console.log("实例化...");
}
}
let user1 = new User();

编译后的 JS 文件内容如下:
在这里插入图片描述

  • 默认情况下,构造函数是一个空函数

  • 构造函数会在类被实例化的时候调用

  • 我们定义的构造函数会覆盖默认构造函数

  • 如果在实例化(new)一个类的时候无需传入参数,则可以省略 ()

  • 构造函数 constructor 不允许有return返回值类型标注的(因为要返回实例对象)

成员属性和方法

通常情况下,我们会把一个类实例化的时候的初始化相关代码写在构造函数中,比如对类成员属性的初始化赋值

在这里插入图片描述
调用成员方法的例子如下:
在这里插入图片描述

this 关键字调用成员属性和方法

在类内部,我们可以通过 this 关键字来访问类的成员属性和方法
在这里插入图片描述

构造函数参数属性

因为在构造函数中对类成员属性进行传参赋值初始化是一个比较常见的场景,所以 ts 提供了一个简化操作:给构造函数参数添加修饰符来直接生成成员属性

  • public 就是类的默认修饰符,表示该成员可以在任何地方进行读写操作
    在这里插入图片描述

public 其实帮我们干了两件事情

  • 添加成员属性
  • 实例化的时候,赋值
    在这里插入图片描述

继承

继承通过关键字 extends 来声明

这里新建一个继承 UserVIP 的类,

在这里插入图片描述

super 关键字

在子类中,我们可以通过 super 来引用父类

  • 如果子类没有重写构造函数,则会在默认的 constructor 中调用 super()

  • 如果子类有自己的构造函数,则需要在子类构造函数中显示的调用父类构造函数 : super(//参数),否则会报错

  • 在子类构造函数中只有在 super(//参数) 之后才能访问 this

  • 在子类中,可以通过 super 来访问父类的成员属性和方法

  • 通过 super 访问父类的的同时,会自动绑定上下文对象为当前子类 this

1
2
3
4
5
6
7
8
9
10
11
12
13
class VIP extends User {
constructor(id: number, username: string, public score = 0) {
super(id, username);
}

postAttachment(file: string): void {
console.log(`${this.username} 上传了一个附件: ${file}`);
}
}

let vip1 = new VIP(1, "Leo");
vip1.postArticle("标题", "内容");
vip1.postAttachment("1.png");

函数的重写

在这里插入图片描述

函数的重载

在这里插入图片描述

类的修饰符

有的时候,我们希望对类成员(属性、方法)进行一定的访问控制,来保证数据的安全,通过 类修饰符 可以做到这一点,目前 TypeScript 提供了四种修饰符:

  • public:公有,默认
  • protected:受保护
  • private:私有
  • readonly:只读

他们的访问权限如下表格所示:

修饰符 访问范围
public:公有,默认 自身、子类、类外
protected:受保护 自身、子类
private:私有 自身
readonly:只读 自身、子类、类外

寄存器

寄存器有点像 proxy ,他也是通过 set 变量名get 变量名 来处理变量读和写的操作,使用的方式如下图中所示
在这里插入图片描述

静态属性

在这里插入图片描述

在这里插入图片描述

抽象类

有的时候,一个基类(父类)的一些方法无法确定具体的行为,而是由继承的子类去实现,看下面的例子:

现在前端比较流行组件化设计,比如 React

在这里插入图片描述

abstract 关键字

如果一个方法没有具体的实现方法,则可以通过 abstract 关键字进行修饰

1
2
3
4
5
6
7
8
9
10
11
abstract class Component<T1, T2> {

public state: T2;

constructor(
public props: T1
) {
}

public abstract render(): string;
}

使用抽象类有一个好处:

约定了所有继承子类的所必须实现的方法,使类的设计更加的规范

使用注意事项:

  • abstract 修饰的方法不能有方法体
  • 如果一个类有抽象方法,那么该类也必须为抽象的
  • 如果一个类是抽象的,那么就不能使用 new 进行实例化(因为抽象类表名该类有未实现的方法,所以不允许实例化)
  • 如果一个子类继承了一个抽象类,那么该子类就必须实现抽象类中的所有抽象方法,否则该类还得声明为抽象的

泛型

在这里插入图片描述

定义好结构之后就会有响应的提示了

在这里插入图片描述

类与接口

通过接口,我们可以为对象定义一种结构和契约。我们还可以把接口与类进行结合,通过接口,让类去强制符合某种契约,从某个方面来说,当一个抽象类中只有抽象的时候,它就与接口没有太大区别了,这个时候,我们更推荐通过接口的方式来定义契约

  • 抽象类编译后还是会产生实体代码,而接口不会
  • TypeScript 只支持单继承,即一个子类只能有一个父类,但是一个类可以实现过个接口
  • 接口不能有实现,抽象类可以

目的是规范化类的设计

在这里插入图片描述

类与对象类型

当我们在 TypeScript 定义一个类的时候,其实同时定义了两个不同的类型

  • 类类型(构造函数类型)
  • 对象类型

首先,对象类型好理解,就是我们的 new 出来的实例类型

那类类型是什么,我们知道 JavaScript 中的类,或者说是 TypeScript 中的类其实本质上还是一个函数,当然我们也称为构造函数,那么这个类或者构造函数本身也是有类型的,那么这个类型就是类的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Person {
// 属于类的
static type = "人";

// 属于实例的
name: string;
age: number;
gender: string;

// 类的构造函数也是属于类的
constructor(name: string, age: number, gender: "男" | "女" = "男") {
this.name = name;
this.age = age;
this.gender = gender;
}

public eat(): void {
// ...
}
}

let p1 = new Person("zMouse", 35, "男");
p1.eat();
Person.type;

上面例子中,有两个不同的数据

  • Person 类(构造函数)
  • 通过 Person 实例化出来的对象 p1

对应的也有两种不同的类型

  • 实例的类型(Person
  • 构造函数的类型(typeof Person

用接口的方式描述如下

1
2
3
4
5
6
7
8
9
10
11
12
interface Person {
name: string;
age: number;
gender: string;
eat(): void;
}

interface PersonConstructor {
// new 表示它是一个构造函数
new (name: string, age: number, gender: "男" | "女"): PersonInstance;
type: string;
}

在使用的时候要格外注意

1
2
3
4
5
6
7
8
9
function fn1(arg: Person /*如果希望这里传入的Person 的实例对象*/) {
arg.eat();
}
fn1(new Person("", 1, "男"));

function fn2(arg: typeof Person /*如果希望传入的Person构造函数*/) {
new arg("", 1, "男");
}
fn2(Person);