← 返回目录

第 2.3 章:类与面向对象

熟悉的 OOP 概念,TypeScript 风味的实现

📦 类的基础

TypeScript 的类语法与 Java/C++ 非常接近,但底层编译为 JavaScript 的原型链机制。

class Animal {
    name: string;
    sound: string;

    constructor(name: string, sound: string) {
        this.name = name;
        this.sound = sound;
    }

    speak(): string {
        return `${this.name} says ${this.sound}!`;
    }
}

const cat = new Animal("小猫", "喵");
console.log(cat.speak());  // "小猫 says 喵!"

💡 与 Java/C++ 的关键区别:

TypeScript 的类属性必须在类体中声明或在构造函数中通过参数属性简写声明。不像 Java 那样可以直接在构造函数中使用未声明的字段。

🔒 访问修饰符

TypeScript 提供三个访问修饰符,与 Java/C++ 的概念一致。

class BankAccount {
    public owner: string;          // 公开(默认)
    protected balance: number;     // 子类可访问
    private pin: string;           // 仅类内部可访问
    readonly accountId: string;    // 只读,初始化后不可修改

    constructor(owner: string, balance: number, pin: string) {
        this.owner = owner;
        this.balance = balance;
        this.pin = pin;
        this.accountId = crypto.randomUUID();
    }

    public getBalance(): number { return this.balance; }
    private validatePin(input: string): boolean { return input === this.pin; }

    public withdraw(amount: number, pin: string): boolean {
        if (!this.validatePin(pin) || amount > this.balance) return false;
        this.balance -= amount;
        return true;
    }
}

ES 原生私有字段(运行时真正隔离):

class SecureVault {
    #secret: string;  // 真正的私有字段,运行时也无法从外部访问

    constructor(secret: string) {
        this.#secret = secret;
    }

    reveal(): string {
        return this.#secret;
    }
}

const vault = new SecureVault("密码");
// vault.#secret;  // ❌ 语法错误:运行时和编译时都不允许

🔄 与 Java 的对比:

TS 的 private 是编译时检查,运行时仍可通过 (obj as any).pin 访问。如需运行时隔离,使用 #field 语法(ES2022 私有字段)。

🔄 与 Python 的对比:

Python 用 _(约定私有)和 __(名称改写)来表示访问级别,TS 有显式的 public/private/protected 关键字,在编译期严格检查。

✨ 参数属性简写

这是 TypeScript 特有的语法糖——在构造函数参数前加访问修饰符,自动声明并赋值同名属性。

// ❌ 传统写法需要:声明属性 → 构造函数参数 → 手动赋值(三步)
// ✅ 参数属性简写:一步到位
class User {
    constructor(
        public name: string,
        private age: number,
        readonly email: string,
    ) {}

    getAge(): number {
        return this.age;
    }
}

💡 实战建议:

参数属性简写在简单数据类中极为常用。但如果构造函数有复杂逻辑(如参数校验),混用简写和手动赋值可能降低可读性。

🧬 继承

使用 extends 关键字实现类继承,与 Java 的单继承模型一致。

class Animal {
    constructor(public name: string, protected sound: string) {}
    speak(): string { return `${this.name}: ${this.sound}`; }
}

class Dog extends Animal {
    constructor(name: string, public breed: string) {
        super(name, "汪汪");  // 必须调用 super()
    }

    speak(): string {
        return `🐕 ${super.speak()}!(品种:${this.breed})`;
    }

    fetch(item: string): string {
        return `${this.name} 捡回了 ${item}`;
    }
}

const dog = new Dog("旺财", "金毛");
console.log(dog.speak());   // "🐕 旺财: 汪汪!(品种:金毛)"
console.log(dog.fetch("飞盘"));  // "旺财 捡回了 飞盘"

🎨 抽象类

抽象类不能直接实例化,用于定义子类必须实现的模板方法。

abstract class Shape {
    abstract area(): number;
    abstract perimeter(): number;

    describe(): string {
        return `面积: ${this.area().toFixed(2)}, 周长: ${this.perimeter().toFixed(2)}`;
    }
}

class Circle extends Shape {
    constructor(private radius: number) { super(); }
    area(): number { return Math.PI * this.radius ** 2; }
    perimeter(): number { return 2 * Math.PI * this.radius; }
}

class Rectangle extends Shape {
    constructor(private width: number, private height: number) { super(); }
    area(): number { return this.width * this.height; }
    perimeter(): number { return 2 * (this.width + this.height); }
}

// const s = new Shape();        // ❌ 不能实例化抽象类
const circle = new Circle(5);
console.log(circle.describe());  // "面积: 78.54, 周长: 31.42"

🔌 接口实现

implements 显式声明类遵循某个接口契约。一个类可以实现多个接口。

interface Serializable {
    serialize(): string;
}

interface Printable {
    print(): void;
}

class Document implements Serializable, Printable {
    constructor(public title: string, public content: string) {}

    serialize(): string {
        return JSON.stringify({ title: this.title, content: this.content });
    }

    print(): void {
        console.log(`=== ${this.title} ===\n${this.content}`);
    }
}

// 接口约束函数参数
function save(item: Serializable): void {
    const data = item.serialize();
    console.log("保存数据:", data);
}

const doc = new Document("笔记", "TypeScript 很棒");
save(doc);

💡 extends vs implements:

  • extends:继承一个类,获得其实现代码
  • implements:声明遵循接口契约,必须自己实现所有方法
  • 可以同时使用:class A extends B implements C, D

⚡ 静态成员

静态属性和方法属于类本身,不属于实例。常用于工厂模式和工具方法。

class IdGenerator {
    private static nextId: number = 1;

    static generate(): number {
        return IdGenerator.nextId++;
    }

    static reset(): void {
        IdGenerator.nextId = 1;
    }
}

console.log(IdGenerator.generate());  // 1
console.log(IdGenerator.generate());  // 2

// 单例模式
class Config {
    private static instance: Config | null = null;
    private constructor(public readonly env: string) {}

    static getInstance(): Config {
        if (!Config.instance) {
            Config.instance = new Config(process.env.NODE_ENV ?? "development");
        }
        return Config.instance;
    }
}

🎭 装饰器简介

装饰器是一种特殊语法,用于在不修改原始代码的情况下增强类和方法的行为。在 NestJS、Angular 等框架中广泛使用。

// 方法装饰器:自动记录方法调用日志
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const original = descriptor.value;
    descriptor.value = function (...args: any[]) {
        console.log(`调用 ${propertyKey}(${args.join(", ")})`);
        const result = original.apply(this, args);
        console.log(`${propertyKey} 返回: ${result}`);
        return result;
    };
}

class UserService {
    @log
    findById(id: number): string {
        return `User_${id}`;
    }
}

const service = new UserService();
service.findById(42);
// 输出:
// 调用 findById(42)
// findById 返回: User_42

⚠️ 注意事项:

TypeScript 5.0+ 支持 TC39 Stage 3 装饰器标准。旧版实验性装饰器需要在 tsconfig.json 中启用 "experimentalDecorators": true。NestJS 和 Angular 目前仍使用旧版语法。

📝 本章要点

类基础

属性声明、构造函数、方法——语法与 Java/C++ 相似,编译为 JS 原型链

访问修饰符

public/private/protected/readonly#field 提供运行时隔离

参数属性简写

构造函数参数加修饰符,自动声明+赋值——TypeScript 独有的语法糖

继承与抽象类

extends 单继承;abstract 定义模板方法,子类必须实现

接口实现

implements 声明契约,支持多接口实现

静态成员与装饰器

静态成员用于工厂/单例;装饰器增强类行为,框架中广泛使用