一、是什么类
类(Class)是面向对象编程(OOP)的核心概念,它为信息封装提供了基础结构。
类是一种自定义的引用类型,通常称为类类型,它允许开发者创建具有特定属性和方法的对象蓝图。
传统的面向对象语言大多以类为基石,而JavaScript则采用了基于原型的继承模型,这给开发者带来了一定的学习曲线。
随着ES6的推出,JavaScript引入了 class
关键字,虽然它在底层仍然是基于构造函数,但提供了更简洁的语法,使得类的设计更加直观。
然而,JavaScript的 class
仍然缺少一些面向对象语言中的特性,例如修饰符和抽象类。
TypeScript扩展了JavaScript的 class
,完整支持面向对象编程的所有特性,包括类、接口、修饰符和抽象类等,为开发者提供了更丰富的面向对象编程能力。
二、使用方式
定义类的关键字为 class
,后面紧跟类名,类可以包含以下几个模块(类的数据成员):
- 字段 : 字段是类里面声明的变量。字段表示对象的有关数据。
- 构造函数: 类实例化时调用,可以为类的对象分配内存。
- 方法: 方法为对象要执行的操作
如下例子:
class Car {
// 字段
engine:string;
// 构造函数
constructor(engine:string) {
this.engine = engine
}
// 方法
disp():void {
console.log("发动机为 : "+this.engine)
}
}
继承
类的继承使用过extends
的关键字
class Animal {
move(distanceInMeters: number = 0): void {
console.log(`Animal moved ${
distanceInMeters}m.`);
}
}
class Dog extends Animal {
bark(): void {
console.log('Woof! Woof!');
}
performActions(distanceInMeters: number = 0): void {
this.bark();
this.move(distanceInMeters);
this.bark();
}
}
const dog = new Dog();
dog.performActions(10);
Dog
是一个 派生类,它派生自 Animal
基类,派生类通常被称作子类,基类通常被称作 超类
Dog
类继承了Animal
类,因此实例dog
也能够使用Animal
类move
方法
同样,类继承后,子类可以对父类的方法重新定义,这个过程称之为方法的重写,通过super
关键字是对父类的直接引用,该关键字可以引用父类的属性和方法,如下:
class PrinterClass {
doPrint():void {
console.log("父类的 doPrint() 方法。")
}
}
class StringPrinter extends PrinterClass {
doPrint():void {
super.doPrint() // 调用父类的函数
console.log("子类的 doPrint()方法。")
}
}
修饰符
可以看到,上述的形式跟ES6
十分的相似,typescript
在此基础上添加了三种修饰符:
- 公共 public:可以自由的访问类程序里定义的成员
- 私有 private:只能够在该类的内部进行访问
- 受保护 protect:除了在该类的内部可以访问,还可以在子类中仍然可以访问
私有修饰符
只能够在该类的内部进行访问,实例对象并不能够访问
并且继承该类的子类并不能访问,如下图所示:
受保护修饰符
跟私有修饰符很相似,实例对象同样不能访问受保护的属性,如下:
有一点不同的是 protected
成员在子类中仍然可以访问
除了上述修饰符之外,还有只读修饰符
只读修饰符
通过readonly
关键字进行声明,只读属性必须在声明时或构造函数里被初始化,如下:
除了实例属性之外,同样存在静态属性
静态属性
这些属性存在于类本身上面而不是类的实例上,通过static
进行定义,访问这些属性需要通过 类型.静态属性 的这种形式访问,如下所示:
class Square {
static width = '100px'
}
console.log(Square.width) // 100px
上述的类都能发现一个特点就是,都能够被实例化,在 typescript
中,还存在一种抽象类
抽象类
抽象类做为其它派生类的基类使用,它们一般不会直接被实例化,不同于接口,抽象类可以包含成员的实现细节
abstract
关键字是用于定义抽象类和在抽象类内部定义抽象方法,如下所示:
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log('roaming the earch...');
}
}
这种类并不能被实例化,通常需要我们创建子类去继承,如下:
class Cat extends Animal {
makeSound() {
console.log('miao miao')
}
}
const cat = new Cat()
cat.makeSound() // miao miao
cat.move() // roaming the earch...
三、应用场景
除了日常借助类的特性完成日常业务代码,还可以将类(class)也可以作为接口,尤其在 React
工程中是很常用的,如下:
export default class Carousel extends React.Component<Props, State> {
}
由于组件需要传入 props
的类型 Props
,同时有需要设置默认 props
即 defaultProps
,这时候更加适合使用class
作为接口
先声明一个类,这个类包含组件 props
所需的类型和初始值:
// props的类型
export default class Props {
public children: Array<React.ReactElement<any>> | React.ReactElement<any> | never[] = []
public speed: number = 500
public height: number = 160
public animation: string = 'easeInOutQuad'
public isAuto: boolean = true
public autoPlayInterval: number = 4500
public afterChange: () => {
}
public beforeChange: () => {
}
public selesctedColor: string
public showDots: boolean = true
}
当我们需要传入 props
类型的时候直接将 Props
作为接口传入,此时 Props
的作用就是接口,而当需要我们设置defaultProps
初始值的时候,我们只需要:
public static defaultProps = new Props()
Props
的实例就是 defaultProps
的初始值,这就是 class
作为接口的实际应用,我们用一个 class
起到了接口和设置初始值两个作用,方便统一管理,减少了代码量