TypeScript 教程:类与对象 - 5.1 类的基本语法

在 TypeScript 中,类是面向对象编程的核心概念之一。类提供了一种创建对象的蓝图,允许我们定义对象的属性和方法。TypeScript 的类语法与 JavaScript 的类语法相似,但增加了类型注解和其他功能,使得代码更加严谨和可维护。

1. 类的基本定义

在 TypeScript 中,类的定义使用 class 关键字。基本的类定义如下:

class Person {
    name: string;
    age: number;

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

    greet() {
        console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
    }
}

1.1 代码解析

  • class 关键字:用于定义一个类。
  • 属性nameage 是类的属性,分别表示人的名字和年龄。
  • 构造函数constructor 是一个特殊的方法,用于初始化类的实例。它接受参数并将其赋值给类的属性。
  • 方法greet 是一个实例方法,用于输出问候信息。

1.2 优点

  • 清晰的结构:类的定义使得代码结构清晰,易于理解。
  • 封装性:通过类,可以将数据和操作数据的方法封装在一起,增强了代码的模块化。

1.3 缺点

  • 学习曲线:对于初学者,理解类和对象的概念可能需要一定的时间。
  • 性能开销:类的实例化可能会带来一定的性能开销,尤其是在频繁创建对象的场景中。

1.4 注意事项

  • 确保在构造函数中正确初始化所有属性,以避免未定义的行为。
  • 使用访问修饰符(如 publicprivateprotected)来控制属性和方法的可见性。

2. 访问修饰符

TypeScript 提供了三种访问修饰符来控制类成员的可见性:

  • public:默认修饰符,表示该成员可以在任何地方访问。
  • private:表示该成员只能在类内部访问。
  • protected:表示该成员可以在类内部和子类中访问。

2.1 示例代码

class Animal {
    private species: string;
    protected age: number;
    public name: string;

    constructor(species: string, age: number, name: string) {
        this.species = species;
        this.age = age;
        this.name = name;
    }

    public getSpecies(): string {
        return this.species;
    }

    protected makeSound(): void {
        console.log(`${this.name} makes a sound.`);
    }
}

class Dog extends Animal {
    constructor(name: string, age: number) {
        super('Dog', age, name);
    }

    public bark(): void {
        this.makeSound();
        console.log(`${this.name} barks!`);
    }
}

const dog = new Dog('Buddy', 3);
dog.bark(); // 输出: Buddy makes a sound. Buddy barks!
console.log(dog.name); // 输出: Buddy
// console.log(dog.species); // 错误: Property 'species' is private and only accessible within class 'Animal'.

2.2 优点

  • 封装性:通过使用访问修饰符,可以隐藏类的内部实现,保护数据不被外部直接访问。
  • 继承性protected 修饰符允许子类访问父类的属性和方法,增强了代码的复用性。

2.3 缺点

  • 复杂性:过多的访问修饰符可能会使代码变得复杂,增加理解的难度。
  • 灵活性privateprotected 的使用可能会限制某些情况下的灵活性,特别是在需要扩展类的功能时。

2.4 注意事项

  • 在设计类时,合理使用访问修饰符,以确保类的封装性和可维护性。
  • 避免过度使用 public,以防止外部代码对类内部状态的直接修改。

3. 类的继承

TypeScript 支持类的继承,允许一个类扩展另一个类的功能。通过继承,子类可以重用父类的属性和方法。

3.1 示例代码

class Vehicle {
    public brand: string;

    constructor(brand: string) {
        this.brand = brand;
    }

    public honk(): void {
        console.log(`${this.brand} honks!`);
    }
}

class Car extends Vehicle {
    public model: string;

    constructor(brand: string, model: string) {
        super(brand); // 调用父类构造函数
        this.model = model;
    }

    public displayInfo(): void {
        console.log(`This car is a ${this.brand} ${this.model}.`);
    }
}

const myCar = new Car('Toyota', 'Corolla');
myCar.honk(); // 输出: Toyota honks!
myCar.displayInfo(); // 输出: This car is a Toyota Corolla.

3.2 优点

  • 代码复用:通过继承,子类可以重用父类的代码,减少重复。
  • 扩展性:可以在子类中添加新的属性和方法,增强功能。

3.3 缺点

  • 复杂性:过度使用继承可能导致类层次结构复杂,难以维护。
  • 耦合性:子类与父类之间的紧密耦合可能导致修改父类时影响到所有子类。

3.4 注意事项

  • 在设计类的继承关系时,确保子类与父类之间的关系是“是一个”的关系。
  • 考虑使用组合而非继承,以减少类之间的耦合。

4. 抽象类

抽象类是不能被实例化的类,通常用于定义其他类的基类。抽象类可以包含抽象方法(没有实现的方法),子类必须实现这些方法。

4.1 示例代码

abstract class Shape {
    abstract area(): number; // 抽象方法

    public display(): void {
        console.log(`The area is: ${this.area()}`);
    }
}

class Circle extends Shape {
    private radius: number;

    constructor(radius: number) {
        super();
        this.radius = radius;
    }

    public area(): number {
        return Math.PI * this.radius * this.radius;
    }
}

const circle = new Circle(5);
circle.display(); // 输出: The area is: 78.53981633974483

4.2 优点

  • 强制实现:通过抽象类,可以强制子类实现特定的方法,确保接口的一致性。
  • 代码组织:抽象类可以帮助组织代码,提供一个清晰的结构。

4.3 缺点

  • 灵活性:抽象类的使用可能会限制某些灵活性,特别是在需要多重继承的情况下。
  • 复杂性:抽象类的设计可能会增加代码的复杂性。

4.4 注意事项

  • 确保抽象类的设计合理,避免过度抽象化。
  • 使用抽象类时,考虑是否真的需要强制子类实现某些方法。

5. 总结

TypeScript 的类与对象系统为开发者提供了强大的工具来构建可维护和可扩展的应用程序。通过合理使用类、访问修饰符、继承和抽象类,开发者可以创建出结构清晰、功能强大的代码。然而,在使用这些特性时,也需要注意其潜在的复杂性和性能开销。希望本教程能帮助你更好地理解 TypeScript 中的类与对象的基本语法及其应用。