面向对象

​ 除了继承JS的所有特性外,TS还在编译阶段引入了类(class)接口(interface)泛型(generics)等语法糖,使我们更容易以面向对象的方式编写代码。

class(类)

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
class Person{
name: string; // 默认为public
private age: number; // 可用private关键字修饰属性,此时不可直接调用实例的该属性,需要用get方法来获取
readonly sex: string; // readonly表示只读属性,不可被修改
static eyeNum: number = 2; // 用static关键字定义类属性(静态属性)或类方法,可直接通过类访问
protected height: number; // protected修饰的属性只能在当前类与子类中使用
// 构造函数,用来初始化实例属性,在创建对象时会调用
constructor(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
// 方法,获取age属性值
getAge() {
return this.age;
}
static sayHi(){
console.log("Hi!");
}
}
const per = new Person("abc",18,"male");
console.log(per.name); // abc
console.log(per.age); // error
console.log(per.getAge()); // 18
console.log(Person.eyeNum); // 2
console.log(per.sex); // male
per.sex = "female"; // eror
Person.sayHi() // Hi!
1
2
3
4
class animal{
// 可以直接将属性定义在构造函数中,不需要在定义属性与编写构造函数里的内容
constructer(public name: string,private age: number){}
}

extends(继承)

​ 使用extends关键字表示继承,格式为 class 子类 extends 父类{},子类将会拥有父类所有属性与方法

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
class animal {
name: string;
private age: number; // 子类只能通过父类的get方法访问private属性
constructer(name: string,age: number){
this.name = name;
this.age = age;
}
sayHi(){
console.log("Hi!");
}
getAge(){
return this.age;
}
}
class dog extends animal{
weight: number;
constructer(name, age, weight){
// super 表示父类,这里表示执行父类的构造函数
// 若子类重写了构造函数则必须调用父类的构造函数,即super()
super(name, age);
this.weight = weight;
}
// 方法重写,子类覆盖父类方法
satyHi(){
console.log("汪汪");
}
}
class cat extends animal{
sayHi(){
console.log("喵喵");
}
}
const dog1 = new dog("dog1",5,15);
const cat1 = new cat("cat1",3);
console.log(dog1.getAge()); // 5
dog1.sayHi(); // 汪汪
cat1.sayHi(); // 喵喵

abstract class(抽象类)

​ 使用abstract关键字修饰class类,使之成为抽象类,抽象类具有以下特征:

  • 不能用来创建对象,是专门用来被继承的类;
  • 可以添加抽象方法,使用abstract修饰方法;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
abstract class animal {
name: string;
constructor(name: string){
this.name = name;
}
// 抽象方法没有方法体,子类必须对抽象方法进行重写
abstract sayHello(): void;
}
class dog extends animal {
sayHello(){
console.log("wangwang");
}
}
const dog1 = new dog("dog1");
const ani = new animal("ani"); // error
dog1.sayHello(); // wangwang

interface(接口)

​ 使用interface关键字声明接口,与普通的类型声明类似

1
2
3
4
5
6
7
8
9
10
// 类型声明
type myType = {
name: string;
}
// 接口
interface myInterface {
name: string;
}
const person1: myType ={name: "aaa"};
const person2: myInterface = {name:"bbb"};

​ 但类型声明不能重复被声明,接口可以重复声明,两个接口的并集为最终的接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type myType = {
name: string;
}
type myType = { // error
age: number;
}
interface myInterface {
name: string;
}
interface myInterface {
age: number;
}
const person1: myType ={
name: "aaa"
};
const person2: myInterface = {
name: "bbb";
age: 18;
};

​ 接口可以在定义类型的时候限制类的结构,只定义对象的结构,其中的属性不能有实际的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface myInter{
name: string;
sayHello(): void; // 接口中所有方法都是抽象方法
}
// 定义类时可以使类实现一个接口,满足接口的要求
// 该类中必须具有接口的所有属性与方法
class person implements myInter{
name: string;
constructer(name){
this.name = name;
}
sayHello(){
console.log("Hello");
}
}

属性的封装

​ 属性可以被任意修改会导致对象中的数据变得非常不安全,使用封装可以保证数据的一致性与完整性

​ 例如,一个 age 属性可能需要限制在 [0, 150] 之间,如果允许外部直接 person.age = -10,就会导致不合理的状态。

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
class person {
private age: number;
constructer(age: number){
this.age = age;
}
// getter用来读取属性,setter用来修改属性,被称为属性的存取器
// 写法一
getAge(){
return this.age;
}
setAge(value: number){
if(value>=0 && value<=150){
this.age = value
}
}
// 写法二
get age(){
return this.age;
}
set age(value){
if(value>=0 && value<=150){
this.age = value
}
}
}
const per = new person(18);
// 写法一对应的写法
console.log(per.getAge); // 18
console.log(per.setAge(20)); // 20
// 写法二对应的写法
console.log(per.age)
per.age = 20;

泛型

泛型可以使一个函数能处理多种不同类型的数据,并保证输入输出类型一致时

1
2
3
4
5
function fn<T,K>(a: T,b: K): T{
return a;
}
fn(10); // 不指定泛型,TS可自动对类型进行推断
fn<string,number>("abc",123); // 也可指定泛型

​ 也可与接口结合使用

1
2
3
4
5
6
interface IN{
length: number;
}
function fn<T extends IN>(a: T):number{
return a.length;
}

​ 定义时也可使用泛型

1
2
3
4
5
6
class person<T>{
name:T;
constructer(name: T){
this.name = name;
}
}