TypeScript 从零基础到精通(四):面向对象编程(类与继承)

摘要:面向对象编程(OOP)是现代软件开发的重要范式。TypeScript 在 JavaScript 原型链的基础上,提供了完整的类(Class)语法和增强特性:访问修饰符(public/private/protected)、抽象类、静态成员、类与接口的配合等。本文从零讲解 TypeScript 中的类,并结合封装、继承、多态三大特性,帮助你写出结构清晰、可复用的代码。


一、前言

前三篇文章中,我们学习了 TypeScript 的基础类型、函数、对象和接口。现在我们可以为普通的 JavaScript 代码加上类型注解,用接口定义对象结构。

但是,当项目规模增长,我们需要更强大的组织代码的方式------面向对象编程(OOP)。OOP 将数据和对数据的操作封装在一起(类),通过继承实现代码复用,通过多态应对变化。TypeScript 让 JavaScript 的类更加强大和严谨。


二、JavaScript 类的演变

ES6(ES2015)为 JavaScript 引入了 class 语法糖,但它本质上还是基于原型链。TypeScript 在此基础上增加了类型系统和面向对象设计的增强特性。

ES6 类示例

TypeScript 复制代码
class Animal {
  constructor(name) {
    this.name = name;
  }
  speak() {
    console.log(`${this.name} makes a sound.`);
  }
}

问题 :ES6 类没有真正的私有成员(只能通过约定 _nameSymbol 模拟),也没有类型约束。

TypeScript 填补了这些空白。


三、TypeScript 中的类基础

3.1 类的基本定义

使用 class 关键字定义类,属性需要提前声明类型(不像 JavaScript 动态添加)。

TypeScript 复制代码
class Person {
  name: string;      // 声明属性(实例属性)
  age: number;
​
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
​
  greet(): void {
    console.log(`Hello, I'm ${this.name}, ${this.age} years old.`);
  }
}
​
const p = new Person("Alice", 30);
p.greet();   // Hello, I'm Alice, 30 years old.

注意 :TypeScript 要求在构造函数中明确初始化所有声明了的属性,否则会报错(除非开启 strictPropertyInitialization 或使用非空断言 !)。

3.2 构造函数(constructor)

构造函数在 new 时执行,用于初始化实例。参数和返回值的类型注解与普通函数一样。

TypeScript 复制代码
class Student {
  name: string;
  grade: number;
​
  constructor(name: string, grade: number) {
    this.name = name;
    this.grade = grade;
  }
}

如果参数带有默认值或修饰符(见下文),可以使用更简洁的写法。

3.3 实例属性与方法

实例属性就是 this.xxx,实例方法直接定义在类内部。

TypeScript 复制代码
class Counter {
  count: number = 0;   // 可以直接赋初始值
​
  increment(): void {
    this.count++;
  }
​
  getValue(): number {
    return this.count;
  }
}

四、访问修饰符:public、private、protected

TypeScript 提供了三个修饰符来控制类成员的可见性。

修饰符 含义
public 默认值,任何地方都可访问
private 只在当前类内部可访问,子类和实例不能访问
protected 在当前类和子类内部可访问,实例不能访问

4.1 基本用法

TypeScript 复制代码
class Animal {
  public name: string;          // 公有(默认)
  private age: number;          // 私有
  protected species: string;    // 受保护
​
  constructor(name: string, age: number, species: string) {
    this.name = name;
    this.age = age;
    this.species = species;
  }
​
  private getAge(): number {    // 私有方法
    return this.age;
  }
​
  public showInfo(): void {
    console.log(`${this.name}, age=${this.age}, species=${this.species}`);
    console.log(this.getAge());  // 内部可以调用私有方法
  }
}
​
const dog = new Animal("旺财", 3, "Canine");
console.log(dog.name);      // ✅ 公有,可访问
// console.log(dog.age);     // ❌ 私有,报错
// console.log(dog.species); // ❌ 受保护,实例不能访问
// dog.getAge();             // ❌ 私有方法

子类中的访问

TypeScript 复制代码
class Dog extends Animal {
  bark(): void {
    console.log(`${this.name} bark!`);   // ✅ 公有可以
    // console.log(this.age);             // ❌ 私有,子类也不能访问
    console.log(this.species);           // ✅ 受保护,子类可访问
  }
}

4.2 参数属性(Parameter Properties)------ 简化写法

TypeScript 提供了一种语法糖:在构造函数参数前直接加上修饰符,TS 会自动声明同名的实例属性并赋值。

TypeScript 复制代码
class User {
  // 等价于先声明 public name: string; 然后在构造函数中 this.name = name
  constructor(public name: string, private age: number) {}
​
  info(): string {
    return `${this.name}, ${this.age}`;
  }
}
​
const u = new User("张三", 25);
console.log(u.name);    // ✅ 公有
// console.log(u.age);  // ❌ 私有

这是非常实用的写法,可以减少重复代码。


五、继承(Inheritance)

继承是面向对象的核心之一,允许子类复用父类的属性和方法,并添加自己的特性。

5.1 extends 关键字

使用 extends 实现继承。

TypeScript 复制代码
class Vehicle {
  constructor(public brand: string, public speed: number) {}
​
  move(): void {
    console.log(`${this.brand} is moving at ${this.speed} km/h.`);
  }
}
​
class Car extends Vehicle {
  doors: number;
  constructor(brand: string, speed: number, doors: number) {
    super(brand, speed);   // 必须先调用 super()
    this.doors = doors;
  }
​
  honk(): void {
    console.log(`${this.brand} horn: Beep beep!`);
  }
}
​
const myCar = new Car("Toyota", 120, 4);
myCar.move();   // 继承自父类
myCar.honk();   // 自己的方法

关键点

  • 子类构造函数中必须调用 super(...) 执行父类构造函数。

  • super 必须在访问 this 之前调用。

5.2 方法重写(Override)

子类可以重写父类的方法,覆盖其行为。同时可以在重写的方法中使用 super.method() 调用父类版本。

TypeScript 复制代码
class Animal {
  speak(): void {
    console.log("Animal makes a sound.");
  }
}
​
class Cat extends Animal {
  speak(): void {
    console.log("Meow!");
  }
}
​
class Dog extends Animal {
  speak(): void {
    super.speak();   // 先调用父类方法
    console.log("Woof!");
  }
}
​
new Cat().speak();   // Meow!
new Dog().speak();   // Animal makes a sound.  Woof!

TypeScript 建议在重写的方法上使用 override 关键字(TS 4.3+),便于明确意图和防止拼写错误。

TypeScript 复制代码
class Bird extends Animal {
  override speak(): void {   // 显式标记 override
    console.log("Chirp chirp");
  }
}

如果没有 override 但方法名写错(本意重写却写成了新方法),TS 会给出提示。


六、抽象类(Abstract Class)

抽象类是不能被实例化的类,用作其他类的基类。它可以包含抽象方法(没有实现,必须在子类中实现)和具体实现。

6.1 抽象方法与抽象类

使用 abstract 关键字。

TypeScript 复制代码
abstract class Shape {
  abstract getArea(): number;   // 抽象方法,没有方法体
​
  getType(): string {           // 普通方法可以有实现
    return "Shape";
  }
}
​
// const s = new Shape();   // ❌ 不能实例化抽象类
​
class Circle extends Shape {
  constructor(public radius: number) {
    super();
  }
​
  getArea(): number {       // 必须实现抽象方法
    return Math.PI * this.radius ** 2;
  }
}
​
const c = new Circle(5);
console.log(c.getArea());   // 78.5398...

6.2 抽象类 vs 接口

抽象类 接口
是否可实例化 不适用(仅类型)
是否可以包含实现 可以(普通方法、属性) 不可以(纯声明,TS 中可放属性但无实现)
继承/实现 子类 extends 单个抽象类 类可以实现多个接口
用途 提供公共基础实现,强制子类实现某些行为 定义契约,完全抽离实现

实践建议:当多个类有共同的实现逻辑时用抽象类;仅定义行为规范时用接口。


七、静态成员(Static)

静态成员属于类本身,而不是实例。通过类名直接访问。

TypeScript 复制代码
class MathUtils {
  static PI: number = 3.14159;
​
  static circleArea(radius: number): number {
    return this.PI * radius * radius;   // 静态方法中 this 指向类本身
  }
}
​
console.log(MathUtils.PI);          // 3.14159
console.log(MathUtils.circleArea(5)); // 78.53975

静态成员可以被继承(子类也能访问),但不能通过实例访问。

TypeScript 复制代码
class ExtendedMath extends MathUtils {}
console.log(ExtendedMath.PI);   // 继承得到

八、类与接口(Implements)

接口定义了一个结构契约,类可以通过 implements 关键字来保证自己符合某个接口。

8.1 基本用法

TypeScript 复制代码
interface Drawable {
  draw(): void;
}
​
interface Resizable {
  resize(width: number, height: number): void;
}
​
class Rectangle implements Drawable, Resizable {
  constructor(public width: number, public height: number) {}
​
  draw(): void {
    console.log(`Drawing rectangle ${this.width}x${this.height}`);
  }
​
  resize(w: number, h: number): void {
    this.width = w;
    this.height = h;
  }
}

注意

  • 类可以实现多个接口(逗号分隔)。

  • 如果类没有完全实现接口中的成员,TS 会报错。

8.2 类与接口的区别

接口只描述结构,不包含实现,也不生成运行时代码。类既描述结构又包含实现,会生成 JS 代码。

TypeScript 复制代码
// 接口编译后消失
interface Point { x: number; y: number; }
​
// 类编译后存在
class PointImpl implements Point {
  constructor(public x: number, public y: number) {}
}

8.3 接口继承类

TypeScript 中接口可以继承类,这会继承类的成员(包括私有和受保护成员),但仅用于类型检查。这种模式常用于将类作为"无形接口"。

TypeScript 复制代码
class Control {
  private state: any;
}
​
interface SelectableControl extends Control {
  select(): void;
}
​
class Button extends Control implements SelectableControl {
  select(): void {}
}
​
// 错误:Image 没有继承 Control,缺少私有成员 state
class Image implements SelectableControl {  // ❌
  select(): void {}
}

九、总结

本文完整介绍了 TypeScript 中的面向对象编程能力:

类的基础

  • 属性声明与构造函数

  • 实例方法与属性

访问修饰符

  • public(默认)、private(仅类内)、protected(类内和子类)

  • 参数属性简化代码

继承

  • extends + super 调用父类

  • 方法重写(override 标记)

抽象类

  • abstract 定义不能实例化的基类

  • 抽象方法强制子类实现

静态成员

  • 通过类名直接调用,适用于工具方法或常量

类与接口

  • implements 让类遵守契约

  • 可实现多个接口

  • 接口可继承类(特殊用法)

OOP 三大特性

  • 封装(通过修饰符隐藏内部细节)

  • 继承(复用父类代码)

  • 多态(子类重写方法,同一接口不同实现)


如果这篇文章帮你解决了实操上的困惑,别忘记点击点赞、分享 ,也可以留言告诉我你遇到的其它问题,我会尽快回复。动手练习是掌握编程最快的方法,请务必亲手敲一遍本文的所有示例代码,并截图保存你的成果。你的关注是我坚持原创和细节共享的力量来源,谢谢大家。

相关推荐
shmily麻瓜小菜鸡1 小时前
Bootstrap 4 常用工具类速查表
前端·javascript·bootstrap
CDN3601 小时前
【架构进阶】告别配置漂移!用 NodeNext + Workspace 打造优雅的 TypeScript Monorepo
前端·javascript·typescript
超人不会飞_Jay1 小时前
6.2前端笔记
前端·javascript·笔记
2401_868534782 小时前
常见的 vue面试题目
前端·javascript·vue.js
胡萝卜术2 小时前
从零搭建 NLP Demo:用 ES6 模块化 + DeepSeek API 构建你的第一个 AI 应用
javascript·面试
颂love2 小时前
TypeScript速学
前端·javascript·typescript
凌涘2 小时前
深入理解 JavaScript 执行机制:从执行上下文到调用栈全解析
前端·javascript
用户938515635072 小时前
从模块化到 Prompt 工程:我用 Node.js + LLM 复刻了传统 NLP 的流程
javascript·人工智能·node.js
YAwu112 小时前
手写一个符合 Promise/A+ 规范的 Promise(附完整代码)
前端·javascript