面向对象速查
TypeScript 面向对象部分可以归纳为四个关键词:类、封装、继承和抽象。接口与泛型则进一步帮助我们描述结构和复用类型逻辑。
一、类的基本模板
ts
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
sayHello(): void {
console.log('大家好,我是' + this.name);
}
}
const person = new Person('孙悟空', 18);
person.sayHello();
| 成员 | 作用 |
|---|---|
| 属性 | 保存对象数据 |
| 构造函数 | 创建实例时初始化数据 |
| 方法 | 描述对象行为 |
this |
表示当前实例 |
new |
创建类的实例 |
二、访问修饰符
| 修饰符 | 当前类 | 子类 | 类外部 |
|---|---|---|---|
public |
可以访问 | 可以访问 | 可以访问 |
protected |
可以访问 | 可以访问 | 不可以访问 |
private |
可以访问 | 不可以访问 | 不可以访问 |
示例:
ts
class User {
public name: string;
protected role: string;
private password: string;
constructor(name: string, role: string, password: string) {
this.name = name;
this.role = role;
this.password = password;
}
}
建议:
- 默认公开的成员才使用
public。 - 只允许子类使用的成员使用
protected。 - 内部实现细节使用
private。
三、readonly、getter 和 setter
readonly
ts
class User {
readonly id: number;
constructor(id: number) {
this.id = id;
}
}
readonly 属性初始化后不能重新赋值。
getter 和 setter
ts
class Person {
private _name: string;
constructor(name: string) {
this._name = name;
}
get name(): string {
return this._name;
}
set name(value: string) {
if (value.trim()) {
this._name = value;
}
}
}
记忆方式:
get:读取属性。set:修改属性。
四、静态成员
ts
class MathTools {
static PI = 3.1415926;
static sum(a: number, b: number): number {
return a + b;
}
}
console.log(MathTools.PI);
console.log(MathTools.sum(1, 2));
静态成员属于类本身,通过类名访问,不需要创建实例。
五、继承和方法重写
ts
class Animal {
constructor(public name: string) {}
run(): void {
console.log(this.name + '正在跑');
}
}
class Dog extends Animal {
bark(): void {
console.log(this.name + '在汪汪叫');
}
override run(): void {
console.log('狗正在跑');
}
}
const dog = new Dog('旺财');
dog.run();
dog.bark();
关键语法:
| 语法 | 作用 |
|---|---|
extends |
继承父类 |
super(...) |
调用父类构造函数 |
super.method() |
调用父类方法 |
override |
明确表示重写父类成员 |
六、抽象类
ts
abstract class Animal {
abstract run(): void;
bark(): void {
console.log('动物在叫');
}
}
class Dog extends Animal {
run(): void {
console.log('狗在跑');
}
}
抽象类的特点:
- 不能直接实例化。
- 可以包含普通属性和普通方法。
- 可以包含抽象方法。
- 子类必须实现抽象方法。
七、接口
描述对象结构
ts
interface Person {
name: string;
sayHello(): void;
}
function greet(person: Person): void {
person.sayHello();
}
类实现接口
ts
class Student implements Person {
constructor(public name: string) {}
sayHello(): void {
console.log('我是' + this.name);
}
}
implements 只负责检查结构,不会自动生成接口中的属性和方法。
八、泛型
泛型函数
ts
function identity<T>(value: T): T {
return value;
}
identity(100);
identity('hello');
泛型类
ts
class Box<T> {
constructor(public value: T) {}
}
const numberBox = new Box(100);
const stringBox = new Box('hello');
泛型约束
ts
interface HasLength {
length: number;
}
function getLength<T extends HasLength>(value: T): number {
return value.length;
}
九、接口、抽象类和普通类的区别
| 类型 | 主要作用 | 能否包含具体实现 | 能否直接实例化 |
|---|---|---|---|
| 接口 | 描述结构和契约 | 不能 | 不能 |
| 抽象类 | 提供通用模型和部分实现 | 可以 | 不能 |
| 普通类 | 创建具体对象 | 可以 | 可以 |
十、面向对象设计建议
- 类负责描述对象状态和行为。
- 不要把所有属性都公开,优先保护内部状态。
- 父类放公共逻辑,子类放具体差异。
- 只需要约束结构时优先考虑接口。
- 需要共享实现时再考虑抽象类或继承。
- 重复的类型逻辑优先使用泛型。
十一、本章小结
- 类解决对象创建和行为组织问题。
- 封装解决属性访问和数据保护问题。
- 继承解决公共逻辑复用问题。
- 抽象类解决通用模型和强制实现问题。
- 接口解决结构约束问题。
- 泛型解决类型安全的代码复用问题。