Appearance
TypeScript 类
在 TypeScript 中,类(Class)是面向对象编程的核心概念,它提供了一种组织代码的方式,封装了数据和方法。TypeScript 扩展了 JavaScript 的类语法,添加了类型注解、访问修饰符、抽象类等特性。本文将详细介绍 TypeScript 中的类。
1. 类的定义
基本语法
使用 class 关键字定义类。
typescript
class Person {
// 属性
name: string;
age: number;
// 构造函数
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
// 方法
greet(): string {
return `Hello, my name is ${this.name} and I am ${this.age} years old.`;
}
}
// 创建实例
const person = new Person("John", 30);
console.log(person.greet()); // 输出:Hello, my name is John and I am 30 years old.类的成员
类的成员包括:
- 属性:存储数据
- 方法:执行操作
- 构造函数:初始化实例
- 访问器:getter 和 setter 方法
- 静态成员:属于类本身,而非实例
2. 构造函数
构造函数是类中的特殊方法,用于初始化实例。
基本用法
typescript
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
const person = new Person("John", 30);
console.log(person.name); // 输出:John
console.log(person.age); // 输出:30构造函数参数属性
TypeScript 提供了构造函数参数属性的语法,可以在构造函数参数前添加访问修饰符,自动创建和初始化属性。
typescript
class Person {
// 使用参数属性
constructor(public name: string, public age: number) {
// 不需要手动赋值
}
}
const person = new Person("John", 30);
console.log(person.name); // 输出:John
console.log(person.age); // 输出:30可选参数
构造函数参数可以是可选的。
typescript
class Person {
constructor(public name: string, public age?: number) {
// age 是可选的
}
}
const person1 = new Person("John", 30);
console.log(person1.age); // 输出:30
const person2 = new Person("Jane");
console.log(person2.age); // 输出:undefined默认参数
构造函数参数可以有默认值。
typescript
class Person {
constructor(public name: string, public age: number = 18) {
// age 默认为 18
}
}
const person1 = new Person("John", 30);
console.log(person1.age); // 输出:30
const person2 = new Person("Jane");
console.log(person2.age); // 输出:183. 访问修饰符
TypeScript 提供了三种访问修饰符:
public:公共的,默认值private:私有的,只能在类内部访问protected:受保护的,只能在类内部和子类中访问
基本用法
typescript
class Person {
public name: string; // 公共属性
private age: number; // 私有属性
protected gender: string; // 受保护属性
constructor(name: string, age: number, gender: string) {
this.name = name;
this.age = age;
this.gender = gender;
}
public getAge(): number {
return this.age; // 可以在类内部访问私有属性
}
protected getGender(): string {
return this.gender; // 可以在类内部访问受保护属性
}
}
const person = new Person("John", 30, "male");
console.log(person.name); // 输出:John
// console.log(person.age); // 错误:属性 "age" 是私有的
// console.log(person.gender); // 错误:属性 "gender" 是受保护的
console.log(person.getAge()); // 输出:30子类访问
typescript
class Person {
public name: string;
private age: number;
protected gender: string;
constructor(name: string, age: number, gender: string) {
this.name = name;
this.age = age;
this.gender = gender;
}
private getAge(): number {
return this.age;
}
protected getGender(): string {
return this.gender;
}
}
class Employee extends Person {
constructor(name: string, age: number, gender: string, public employeeId: number) {
super(name, age, gender);
}
getEmployeeInfo(): string {
// return `Name: ${this.name}, Age: ${this.age}`; // 错误:属性 "age" 是私有的
return `Name: ${this.name}, Gender: ${this.getGender()}, ID: ${this.employeeId}`;
}
}
const employee = new Employee("John", 30, "male", 1001);
console.log(employee.getEmployeeInfo()); // 输出:Name: John, Gender: male, ID: 10014. 继承
类可以继承其他类,使用 extends 关键字。
基本继承
typescript
class Person {
constructor(public name: string, public age: number) {}
greet(): string {
return `Hello, my name is ${this.name}.`;
}
}
class Employee extends Person {
constructor(name: string, age: number, public employeeId: number) {
super(name, age); // 调用父类构造函数
}
// 重写父类方法
greet(): string {
return `${super.greet()} I am an employee with ID ${this.employeeId}.`;
}
}
const employee = new Employee("John", 30, 1001);
console.log(employee.greet()); // 输出:Hello, my name is John. I am an employee with ID 1001.方法重写
子类可以重写父类的方法,使用 super 关键字调用父类的方法。
typescript
class Animal {
makeSound(): void {
console.log("Some generic sound");
}
}
class Dog extends Animal {
makeSound(): void {
super.makeSound(); // 调用父类方法
console.log("Woof! Woof!");
}
}
class Cat extends Animal {
makeSound(): void {
console.log("Meow! Meow!");
}
}
const dog = new Dog();
dog.makeSound();
// 输出:
// Some generic sound
// Woof! Woof!
const cat = new Cat();
cat.makeSound();
// 输出:
// Meow! Meow!5. 抽象类
抽象类是不能直接实例化的类,用于定义子类的共同接口。使用 abstract 关键字定义抽象类。
基本用法
typescript
abstract class Animal {
constructor(public name: string) {}
// 抽象方法,子类必须实现
abstract makeSound(): void;
// 普通方法
move(): void {
console.log(`${this.name} is moving`);
}
}
class Dog extends Animal {
makeSound(): void {
console.log("Woof! Woof!");
}
}
class Cat extends Animal {
makeSound(): void {
console.log("Meow! Meow!");
}
}
// const animal = new Animal("Generic Animal"); // 错误:不能创建抽象类的实例
const dog = new Dog("Rex");
dog.makeSound(); // 输出:Woof! Woof!
dog.move(); // 输出:Rex is moving
const cat = new Cat("Whiskers");
cat.makeSound(); // 输出:Meow! Meow!
cat.move(); // 输出:Whiskers is moving抽象属性
抽象类可以定义抽象属性,子类必须实现。
typescript
abstract class Shape {
// 抽象属性
abstract name: string;
// 抽象方法
abstract calculateArea(): number;
// 普通方法
display(): void {
console.log(`This is a ${this.name}`);
}
}
class Circle extends Shape {
name: string = "Circle";
constructor(private radius: number) {
super();
}
calculateArea(): number {
return Math.PI * this.radius * this.radius;
}
}
class Rectangle extends Shape {
name: string = "Rectangle";
constructor(private width: number, private height: number) {
super();
}
calculateArea(): number {
return this.width * this.height;
}
}
const circle = new Circle(5);
circle.display(); // 输出:This is a Circle
console.log(circle.calculateArea()); // 输出:78.53981633974483
const rectangle = new Rectangle(4, 6);
rectangle.display(); // 输出:This is a Rectangle
console.log(rectangle.calculateArea()); // 输出:246. 静态成员
静态成员属于类本身,而非实例,使用 static 关键字定义。
静态属性
typescript
class MathUtils {
static PI: number = 3.14159;
static MAX_NUMBER: number = Number.MAX_SAFE_INTEGER;
}
console.log(MathUtils.PI); // 输出:3.14159
console.log(MathUtils.MAX_NUMBER); // 输出:9007199254740991
// const mathUtils = new MathUtils();
// console.log(mathUtils.PI); // 错误:静态属性只能通过类访问静态方法
typescript
class MathUtils {
static add(a: number, b: number): number {
return a + b;
}
static multiply(a: number, b: number): number {
return a * b;
}
}
console.log(MathUtils.add(5, 3)); // 输出:8
console.log(MathUtils.multiply(5, 3)); // 输出:15静态成员的访问
typescript
class Person {
static species: string = "Homo sapiens";
name: string;
constructor(name: string) {
this.name = name;
}
static getSpecies(): string {
return Person.species;
}
getSpecies(): string {
return Person.species;
}
}
console.log(Person.species); // 输出:Homo sapiens
console.log(Person.getSpecies()); // 输出:Homo sapiens
const person = new Person("John");
console.log(person.getSpecies()); // 输出:Homo sapiens
// console.log(person.species); // 错误:静态属性只能通过类访问7. 访问器
访问器(Accessor)用于控制属性的访问,包括 getter 和 setter 方法。
基本用法
typescript
class Person {
private _name: string;
private _age: number;
constructor(name: string, age: number) {
this._name = name;
this._age = age;
}
// getter
get name(): string {
return this._name;
}
// setter
set name(value: string) {
if (value.length > 0) {
this._name = value;
}
}
// getter
get age(): number {
return this._age;
}
// setter
set age(value: number) {
if (value >= 0) {
this._age = value;
}
}
}
const person = new Person("John", 30);
console.log(person.name); // 输出:John
console.log(person.age); // 输出:30
person.name = "Jane";
person.age = 25;
console.log(person.name); // 输出:Jane
console.log(person.age); // 输出:25
person.name = ""; // 不会改变,因为有验证
person.age = -5; // 不会改变,因为有验证
console.log(person.name); // 输出:Jane
console.log(person.age); // 输出:25只读访问器
typescript
class Person {
private _name: string;
private _age: number;
constructor(name: string, age: number) {
this._name = name;
this._age = age;
}
// 只有 getter,没有 setter,所以是只读的
get name(): string {
return this._name;
}
get age(): number {
return this._age;
}
}
const person = new Person("John", 30);
console.log(person.name); // 输出:John
console.log(person.age); // 输出:30
// person.name = "Jane"; // 错误:没有 setter 方法
// person.age = 25; // 错误:没有 setter 方法8. 类实现接口
类可以实现接口,使用 implements 关键字。
基本实现
typescript
interface Person {
name: string;
age: number;
greet(): string;
}
class Employee implements Person {
constructor(public name: string, public age: number, public employeeId: number) {}
greet(): string {
return `Hello, my name is ${this.name} and I am ${this.age} years old.`;
}
}
const employee = new Employee("John", 30, 1001);
console.log(employee.greet()); // 输出:Hello, my name is John and I am 30 years old.实现多个接口
typescript
interface Person {
name: string;
age: number;
}
interface Greetable {
greet(): string;
}
interface Workable {
work(): string;
}
class Employee implements Person, Greetable, Workable {
constructor(public name: string, public age: number, public employeeId: number) {}
greet(): string {
return `Hello, my name is ${this.name}.`;
}
work(): string {
return `Employee ${this.employeeId} is working.`;
}
}
const employee = new Employee("John", 30, 1001);
console.log(employee.greet()); // 输出:Hello, my name is John.
console.log(employee.work()); // 输出:Employee 1001 is working.9. 类的高级用法
泛型类
类可以使用泛型,使类更加灵活。
typescript
class Container<T> {
private value: T;
constructor(value: T) {
this.value = value;
}
getValue(): T {
return this.value;
}
setValue(value: T): void {
this.value = value;
}
}
// 使用数字类型
const numberContainer = new Container<number>(10);
console.log(numberContainer.getValue()); // 输出:10
numberContainer.setValue(20);
console.log(numberContainer.getValue()); // 输出:20
// 使用字符串类型
const stringContainer = new Container<string>("hello");
console.log(stringContainer.getValue()); // 输出:hello
stringContainer.setValue("world");
console.log(stringContainer.getValue()); // 输出:world
// 使用对象类型
interface Person {
name: string;
age: number;
}
const personContainer = new Container<Person>({ name: "John", age: 30 });
console.log(personContainer.getValue()); // 输出:{ name: 'John', age: 30 }类的继承与接口实现结合
typescript
interface Greetable {
greet(): string;
}
class Person {
constructor(public name: string, public age: number) {}
}
class Employee extends Person implements Greetable {
constructor(name: string, age: number, public employeeId: number) {
super(name, age);
}
greet(): string {
return `Hello, my name is ${this.name} and I am an employee with ID ${this.employeeId}.`;
}
}
const employee = new Employee("John", 30, 1001);
console.log(employee.greet()); // 输出:Hello, my name is John and I am an employee with ID 1001.类的类型
类本身也是一种类型,可以用于类型注解。
typescript
class Person {
constructor(public name: string, public age: number) {}
greet(): string {
return `Hello, my name is ${this.name}.`;
}
}
// 使用类作为类型
function createPerson(person: Person): Person {
return person;
}
const person = new Person("John", 30);
const newPerson = createPerson(person);
console.log(newPerson.greet()); // 输出:Hello, my name is John.
// 使用类的构造函数作为类型
function createPersonInstance(ctor: new (name: string, age: number) => Person, name: string, age: number): Person {
return new ctor(name, age);
}
const person2 = createPersonInstance(Person, "Jane", 25);
console.log(person2.greet()); // 输出:Hello, my name is Jane.10. 类的最佳实践
1. 使用访问修饰符
使用访问修饰符来控制属性和方法的访问权限,提高代码的封装性和安全性。
typescript
// 推荐:使用访问修饰符
class Person {
constructor(private name: string, private age: number) {}
public getName(): string {
return this.name;
}
public setName(name: string): void {
this.name = name;
}
}
// 不推荐:使用公共属性
class Person {
constructor(public name: string, public age: number) {}
}2. 使用构造函数参数属性
使用构造函数参数属性来简化代码。
typescript
// 推荐:使用构造函数参数属性
class Person {
constructor(private name: string, private age: number) {}
}
// 不推荐:手动赋值
class Person {
private name: string;
private age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}3. 使用抽象类
对于具有共同行为的类,使用抽象类来定义共同接口。
typescript
// 推荐:使用抽象类
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log("Moving...");
}
}
class Dog extends Animal {
makeSound(): void {
console.log("Woof! Woof!");
}
}
// 不推荐:重复定义共同方法
class Dog {
makeSound(): void {
console.log("Woof! Woof!");
}
move(): void {
console.log("Moving...");
}
}
class Cat {
makeSound(): void {
console.log("Meow! Meow!");
}
move(): void {
console.log("Moving...");
}
}4. 使用访问器
对于需要验证或计算的属性,使用访问器。
typescript
// 推荐:使用访问器
class Person {
private _age: number;
constructor(private name: string, age: number) {
this._age = age;
}
get age(): number {
return this._age;
}
set age(value: number) {
if (value >= 0) {
this._age = value;
}
}
}
// 不推荐:直接访问属性
class Person {
constructor(private name: string, public age: number) {}
}5. 合理使用静态成员
对于与类相关但与实例无关的属性和方法,使用静态成员。
typescript
// 推荐:使用静态成员
class MathUtils {
static PI: number = 3.14159;
static calculateArea(radius: number): number {
return this.PI * radius * radius;
}
}
// 不推荐:使用实例成员
class MathUtils {
PI: number = 3.14159;
calculateArea(radius: number): number {
return this.PI * radius * radius;
}
}11. 常见错误
1. 忘记调用父类构造函数
在子类中,如果定义了构造函数,必须调用父类的构造函数。
typescript
class Person {
constructor(public name: string) {}
}
// 错误:忘记调用父类构造函数
// class Employee extends Person {
// constructor(name: string, public employeeId: number) {
// // 缺少 super(name);
// }
// }
// 正确
class Employee extends Person {
constructor(name: string, public employeeId: number) {
super(name); // 调用父类构造函数
}
}2. 访问修饰符使用错误
typescript
class Person {
private name: string;
constructor(name: string) {
this.name = name;
}
}
const person = new Person("John");
// console.log(person.name); // 错误:属性 "name" 是私有的3. 抽象类实例化
抽象类不能直接实例化。
typescript
abstract class Animal {
abstract makeSound(): void;
}
// 错误:不能创建抽象类的实例
// const animal = new Animal();
// 正确:创建子类实例
class Dog extends Animal {
makeSound(): void {
console.log("Woof! Woof!");
}
}
const dog = new Dog();
dog.makeSound();4. 方法重写错误
子类重写父类方法时,方法签名必须匹配。
typescript
class Person {
greet(name: string): string {
return `Hello, ${name}!`;
}
}
// 错误:方法签名不匹配
// class Employee extends Person {
// greet(): string { // 缺少参数
// return "Hello!";
// }
// }
// 正确
class Employee extends Person {
greet(name: string): string {
return `Hello, ${name}! I am an employee.`;
}
}5. 静态成员访问错误
静态成员只能通过类访问,不能通过实例访问。
typescript
class MathUtils {
static PI: number = 3.14159;
}
// 错误:静态成员只能通过类访问
// const mathUtils = new MathUtils();
// console.log(mathUtils.PI);
// 正确
console.log(MathUtils.PI);12. 实际应用场景
1. 定义模型类
typescript
class User {
constructor(
public id: number,
public name: string,
public email: string,
private password: string
) {}
getPassword(): string {
return this.password;
}
setPassword(password: string): void {
this.password = password;
}
}
const user = new User(1, "John", "john@example.com", "password123");
console.log(user.name); // 输出:John
console.log(user.email); // 输出:john@example.com
// console.log(user.password); // 错误:属性 "password" 是私有的
console.log(user.getPassword()); // 输出:password1232. 实现业务逻辑
typescript
class ShoppingCart {
private items: { product: string; price: number; quantity: number }[] = [];
addItem(product: string, price: number, quantity: number): void {
this.items.push({ product, price, quantity });
}
removeItem(product: string): void {
this.items = this.items.filter(item => item.product !== product);
}
calculateTotal(): number {
return this.items.reduce((total, item) => total + item.price * item.quantity, 0);
}
getItems(): { product: string; price: number; quantity: number }[] {
return this.items;
}
}
const cart = new ShoppingCart();
cart.addItem("Apple", 1.99, 3);
cart.addItem("Banana", 0.99, 5);
console.log(cart.getItems()); // 输出购物车 items
console.log(cart.calculateTotal()); // 输出:10.92
cart.removeItem("Apple");
console.log(cart.getItems()); // 输出购物车 items
console.log(cart.calculateTotal()); // 输出:4.953. 实现设计模式
单例模式
typescript
class Singleton {
private static instance: Singleton;
private constructor() {
// 私有构造函数,防止外部实例化
}
static getInstance(): Singleton {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
doSomething(): void {
console.log("Doing something...");
}
}
// const singleton = new Singleton(); // 错误:构造函数是私有的
const singleton1 = Singleton.getInstance();
const singleton2 = Singleton.getInstance();
console.log(singleton1 === singleton2); // 输出:true
singleton1.doSomething(); // 输出:Doing something...工厂模式
typescript
interface Shape {
calculateArea(): number;
}
class Circle implements Shape {
constructor(private radius: number) {}
calculateArea(): number {
return Math.PI * this.radius * this.radius;
}
}
class Rectangle implements Shape {
constructor(private width: number, private height: number) {}
calculateArea(): number {
return this.width * this.height;
}
}
class ShapeFactory {
static createShape(type: string, ...args: any[]): Shape {
switch (type) {
case "circle":
return new Circle(args[0]);
case "rectangle":
return new Rectangle(args[0], args[1]);
default:
throw new Error(`Unknown shape type: ${type}`);
}
}
}
const circle = ShapeFactory.createShape("circle", 5);
console.log(circle.calculateArea()); // 输出:78.53981633974483
const rectangle = ShapeFactory.createShape("rectangle", 4, 6);
console.log(rectangle.calculateArea()); // 输出:244. 实现服务类
typescript
class ApiService {
private baseUrl: string;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
async fetchData(endpoint: string): Promise<any> {
const response = await fetch(`${this.baseUrl}/${endpoint}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}
async postData(endpoint: string, data: any): Promise<any> {
const response = await fetch(`${this.baseUrl}/${endpoint}`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}
}
const apiService = new ApiService("https://api.example.com");
// 使用
apiService.fetchData("users").then(data => {
console.log(data);
}).catch(error => {
console.error(error);
});
apiService.postData("users", { name: "John", email: "john@example.com" }).then(data => {
console.log(data);
}).catch(error => {
console.error(error);
});5. 实现工具类
typescript
class DateUtils {
static formatDate(date: Date): string {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
}
static parseDate(dateString: string): Date {
return new Date(dateString);
}
static getDaysBetween(date1: Date, date2: Date): number {
const timeDiff = Math.abs(date2.getTime() - date1.getTime());
return Math.ceil(timeDiff / (1000 * 3600 * 24));
}
}
const today = new Date();
console.log(DateUtils.formatDate(today)); // 输出:2023-10-05(示例)
const dateString = "2023-10-01";
const parsedDate = DateUtils.parseDate(dateString);
console.log(parsedDate); // 输出:2023-10-01T00:00:00.000Z
const date1 = new Date("2023-10-01");
const date2 = new Date("2023-10-05");
console.log(DateUtils.getDaysBetween(date1, date2)); // 输出:4总结
TypeScript 中的类是面向对象编程的核心概念,它提供了一种组织代码的方式,封装了数据和方法。TypeScript 扩展了 JavaScript 的类语法,添加了类型注解、访问修饰符、抽象类等特性。本文介绍了 TypeScript 类的以下内容:
- 类的定义:基本语法,类的成员
- 构造函数:基本用法,构造函数参数属性,可选参数,默认参数
- 访问修饰符:public,private,protected
- 继承:基本继承,方法重写
- 抽象类:基本用法,抽象属性
- 静态成员:静态属性,静态方法,静态成员的访问
- 访问器:getter 和 setter 方法,只读访问器
- 类实现接口:基本实现,实现多个接口
- 类的高级用法:泛型类,类的继承与接口实现结合,类的类型
- 最佳实践:使用访问修饰符,使用构造函数参数属性,使用抽象类,使用访问器,合理使用静态成员
- 常见错误:忘记调用父类构造函数,访问修饰符使用错误,抽象类实例化,方法重写错误,静态成员访问错误
- 实际应用场景:定义模型类,实现业务逻辑,实现设计模式(单例模式、工厂模式),实现服务类,实现工具类
通过合理使用类,你可以在 TypeScript 中构建更加模块化、可维护的代码,提高代码的可读性和可重用性。