TypeScript实用笔记(二):类与接口详解
文章目录
- 一、 constructor()
- 二、 调用父类的构造函数
- 1. 有constructor的继承
- 2. 无constructor的继承
- 三、 接口interface的实现
- 1. 实现接口
- 2. 接口继承
- 3. 总结
- 四、访问修饰符
- 1. private
- 2. protected
- 3. public
- 4. readonly
- 5. abstract
- 6. static
- 7. 访问范围总结
- 7.1 访问范围
- 7.2 示例
TypeScript实用笔记(一):初始化、类型定义与函数使用
TypeScript实用笔记(二):类与接口详解
TypeScript实用笔记(三):泛型的使用 && 的12种工具类型的使用
一、 constructor()
constructor
是一个特殊的方法,用于初始化对象的实例。当创建一个类的实例时,constructor
方法会被自动调用。它的主要作用是设置实例的初始状态,通常包括初始化属性和执行必要的启动代码。- 每个类只能有 <= 一个
constructor
class Car {/*** make 和 model 是 Car 类的属性* 在类中定义这些属性是必要的,以便能够在 constructor 中使用 this.make 和 this.model 进行赋值。* 如果没有这些属性的定义,TypeScript 将会报错,提示无法找到这些属性* 如果属性不设定默认值或者设置为可选项,那么在constructor时必须赋值*/make: string; //设置默认值:make: string | undefined;model: string; //参数设置为可选项:model?: stringconstructor(make: string, model: string) {this.make = make;this.model = model;}
}const myCar = new Car("Toyota", "Corolla");
console.log(myCar.make); // 输出: Toyota
二、 调用父类的构造函数
- 子类可以重写父类的方法,如果不进行重写,则可直接调用。
1. 有constructor的继承
- 显式构造函数:如果子类定义了一个
constructor
,则必须在该构造函数中调用父类的构造函数,通常使用super()
。 - 参数传递:子类的构造函数可以接收参数,并可以将这些参数传递给父类的构造函数。
- 初始化:在子类构造函数中,可以初始化子类特有的属性。
//定义一个父类 Animal
class Animal {//父类属性name: string;//构造函数constructor(name: string) {this.name = name;}//父类方法speak() {console.log(`${this.name} makes a noise.`);}speak2() {console.log(`this is speak2.`);}}//定义一个子类 Dog 继承自 Animal
class Dog extends Animal {//子类特有的属性breed: string;//子类构造函数constructor(name: string, breed: string) {//调用父类的构造函数super(name); //先调用父类的构造函数以初始化父类属性this.breed = breed; //初始化子类特有属性}//重写父类的方法speak() {console.log(`${this.name} barks.`);}
}//创建一个 Animal 实例
const animal = new Animal("Generic Animal", 5);
animal.speak(); //输出: Generic Animal makes a noise.//创建一个 Dog 实例
const dog = new Dog("Buddy", 3, "Golden Retriever");
dog.speak(); //输出: Buddy barks.
dog.speak2(); //输出: this is speak2.
console.log(`${dog.name} is a ${dog.breed}.`); //输出: Buddy is a Golden Retriever.
2. 无constructor的继承
- 隐式构造函数:如果子类没有定义
constructor
,则 JavaScript 引擎会自动创建一个隐式构造函数。在这种情况下,父类的构造函数会被自动调用。 - 无法传递参数:子类将不能接收参数或进行特定的初始化操作,除非父类构造函数接受的参数是默认的。
class Animal {name: string;constructor(name: string) {this.name = name;}
}class Cat extends Animal {//没有显式的 constructormeow() {console.log(`${this.name} says Meow!`);}
}const myCat = new Cat("Whiskers"); //调用父类构造函数. 虽然子类没有显式的constructor,但因为父类有未赋值的初始参数,所以子类调用的时候需要传父类name值
console.log(myCat.name); //输出: Whiskers
myCat.meow(); //输出: Whiskers says Meow!
三、 接口interface的实现
- 定义接口:使用
interface
关键字定义一个接口。 - 实现接口:在类的声明中使用
implements
关键字,表示该类实现了该接口。 - 实现接口中的属性和方法:类++必须实现++接口中定义的所有属性和方法。
1. 实现接口
//定义一个接口
interface Vehicle {make: string; //属性model: string; //属性start(): void; //方法
}//实现接口的类
class Car implements Vehicle {make: string;model: string;constructor(make: string, model: string) {this.make = make;this.model = model;}//实现接口中的方法start() {console.log(`The ${this.make} ${this.model} is starting.`);}
}//创建一个 Car 实例
const myCar = new Car("Toyota", "Corolla");
myCar.start(); //输出: The Toyota Corolla is starting.
2. 接口继承
//定义一个基础接口
interface Engine {start(): void;
}//定义一个更复杂的接口,继承 Engine
interface Vehicle extends Engine {make: string;model: string;
}//实现继承的接口
class Motorcycle implements Vehicle {make: string;model: string;constructor(make: string, model: string) {this.make = make;this.model = model;}start() {console.log(`The ${this.make} ${this.model} motorcycle is starting.`);}
}//创建 Motorcycle 实例
const myBike = new Motorcycle("Honda", "CBR");
myBike.start(); //输出: The Honda CBR motorcycle is starting.
3. 总结
- 接口定义了一组期望的属性和方法,类在实现接口时必须遵循这些规则。这有助于捕捉潜在的错误,并在编译时提供类型检查。
- 类通过实现接口来确保符合特定的结构,提供了一种强类型的方式来定义对象的形状和行为。使用接口使得代码更易于理解和维护,同时也增强了类型安全。
- 接口可以清晰地表示类之间的依赖关系,帮助在测试时更好地理解对象之间的交互。
四、访问修饰符
只能用于++类中的属性和方法++,不能用于其他类型的声明(例如,接口、函数等)。
public
是默认的访问修饰符,若未指定,则默认为public
。
1. private
private
表示类的属性和方法只能在类的内部访问,不能在类的实例或子类中直接访问。- 当一个类被继承时,子类无法直接访问父类中定义的
private
成员。
class BankAccount {private balance: number; // 私有属性constructor(initialBalance: number) {this.balance = initialBalance; // 在构造函数中初始化私有属性}public deposit(amount: number) {if (amount > 0) {this.balance += amount;console.log(`Deposited: ${amount}. New balance: ${this.balance}`);}}public withdraw(amount: number) {if (amount > 0 && amount <= this.balance) {this.balance -= amount;console.log(`Withdrawn: ${amount}. New balance: ${this.balance}`);} else {console.log("Insufficient funds.");}}
}const account = new BankAccount(100);
account.deposit(50); // 输出: Deposited: 50. New balance: 150
// 下面的代码会报错,因为 balance 是私有的
// console.log(account.balance); // Error: Property 'balance' is private and only accessible within class 'BankAccount'.
2. protected
protected
表示类的属性和方法只能在类内部和其子类中访问,外部代码无法直接访问这些成员。
class Animal {protected species: string; // 受保护的属性constructor(species: string) {this.species = species;}protected makeSound() { // 受保护的方法console.log(`${this.species} makes a sound.`);}
}class Dog extends Animal {constructor() {super("Dog");}bark() {this.makeSound(); // 子类可以访问受保护的方法console.log("Woof!");}
}const dog = new Dog();
dog.bark(); // 输出: Dog makes a sound. Woof!// 下面的代码会报错,因为 species 和 makeSound 是受保护的
// console.log(dog.species); // Error: Property 'species' is protected and only accessible within class 'Animal' and its subclasses.
// dog.makeSound(); // Error: Method 'makeSound' is protected and only accessible within class 'Animal' and its subclasses.
3. public
public
是默认的访问修饰符,表示类的属性和方法可以被任何地方访问,包括类的实例、子类和外部代码。
class Person {public name: string; // 公有属性constructor(name: string) {this.name = name;}public greet() { // 公有方法console.log(`Hello, my name is ${this.name}.`);}
}const person = new Person("Alice");
console.log(person.name); // 输出: Alice
person.greet(); // 输出: Hello, my name is Alice.
4. readonly
- 表示该成员是只读的,不能被修改。用于定义常量属性,只接受首次赋值。
class Example {readonly readonlyProperty: string;constructor(str: string) {this.readonlyProperty = str;}
}const example = new Example("I am readonly!");
//example.readonlyProperty = "New Value"; // 错误:只读属性不能被修改
console.log(example.readonlyProperty); // 正确访问
5. abstract
- 用于定义抽象类和抽象方法。抽象类不能被实例化,必须由子类实现抽象方法
- 用于创建一个基本类,提供公共接口给子类。
abstract class Animal {abstract makeSound(): void; //抽象方法,告诉你有一个方法,但是子类自行写,但必须有move() {console.log("Moving...");}
}class Dog extends Animal {makeSound() {console.log("Woof!");}
}const dog = new Dog();
dog.makeSound(); //输出: Woof!
dog.move(); //输出: Moving...
6. static
- 表示该成员是静态的,属于类本身而不是类的实例。不需要实例化Class,就可以直接调用里面的static成员
- 用于定义类级别的属性和方法。
class Example {static staticProperty: string = "I am static!";static staticMethod() {console.log("This is a static method.");}
}console.log(Example.staticProperty); //正确访问静态属性
Example.staticMethod(); //正确调用静态方法
7. 访问范围总结
7.1 访问范围
修饰符 | 类内部 | 子类 | 类实例 | 类外部 |
---|---|---|---|---|
private | ✓ | ❌ | ❌ | ❌ |
protected | ✓ | ✓ | ❌ | ❌ |
abstract | ✓ | ✓ | ❌ | ❌ |
static | ✓ | ✓ | ❌ | ✓ |
readonly | ✓ | ✓ | ✓ | ✓ |
public | ✓ | ✓ | ✓ | ✓ |
7.2 示例
//protected 4种范围示例
class Animal {protected sound: string;constructor(sound: string) {this.sound = sound;}//类内部访问:可以在类内访问 protected 成员makeSound() {console.log(`Animal makes sound: ${this.sound}`);}
}class Dog extends Animal {constructor() {super("Woof");}//子类内部访问:可以在子类中访问父类的 protected 成员bark() {console.log(`Dog barks: ${this.sound}`);}
}const animal = new Animal("Generic Sound");
animal.makeSound(); //正确:类内部方法调用const dog = new Dog();
dog.bark(); //正确:子类内部方法调用//以下访问会报错:无法通过实例直接访问或在类外部访问 protected 成员
console.log(animal.sound); //错误:无法通过类实例访问 protected 成员
console.log(dog.sound); //错误:无法通过子类实例访问 protected 成员