前言
大家好,我是小杨。还记得我刚接触编程时,面对"类"这个概念总是觉得有些抽象。直到在TypeScript中深入使用类之后,我才真正体会到面向对象编程的魅力。今天,我想和大家分享我在实际项目中运用TypeScript类的一些心得和技巧。
类的基础:从蓝图到实物
想象一下你要建房子,首先需要一张蓝图,这张蓝图定义了房子的结构:有几个房间、门窗的位置、水电布局等。然后根据这张蓝图,你可以建造出很多具体的房子。
TypeScript中的类就是这样的"蓝图",而根据类创建的对象就是一栋栋具体的"房子"。
最简单的类
让我从一个最简单的例子开始:
typescript
class Car {
brand: string;
color: string;
constructor(brand: string, color: string) {
this.brand = brand;
this.color = color;
}
drive() {
console.log(`The ${this.color} ${this.brand} is driving.`);
}
}
// 创建类的实例
const myCar = new Car("Toyota", "red");
myCar.drive(); // 输出: The red Toyota is driving.
这就是最基本的类:它有属性(brand、color)和方法(drive)。
类的进阶特性
1. 访问修饰符:控制可见性
访问修饰符就像房子的访问权限:有些区域对所有访客开放,有些只对家人开放,有些则是私密空间。
typescript
class BankAccount {
public readonly accountNumber: string; // 公开但只读
private balance: number; // 完全私有
protected owner: string; // 受保护的
constructor(accountNumber: string, initialBalance: number, owner: string) {
this.accountNumber = accountNumber;
this.balance = initialBalance;
this.owner = owner;
}
public deposit(amount: number): void {
if (amount > 0) {
this.balance += amount;
console.log(`Deposited $${amount}. New balance: $${this.balance}`);
}
}
public withdraw(amount: number): boolean {
if (amount > 0 && amount <= this.balance) {
this.balance -= amount;
console.log(`Withdrew $${amount}. Remaining balance: $${this.balance}`);
return true;
}
console.log("Insufficient funds");
return false;
}
public getBalance(): number {
return this.balance;
}
}
// 使用示例
const myAccount = new BankAccount("123456", 1000, "Alice");
myAccount.deposit(500); // ✅ 允许
console.log(myAccount.accountNumber); // ✅ 允许
// myAccount.balance = 9999; // ❌ 编译错误:balance是私有的
// console.log(myAccount.balance); // ❌ 编译错误
2. 继承:代码复用的艺术
继承让类可以建立在现有类的基础上,就像子承父业一样。
typescript
class Animal {
constructor(public name: string, protected age: number) {}
speak() {
console.log(`${this.name} makes a sound.`);
}
sleep() {
console.log(`${this.name} is sleeping.`);
}
}
class Dog extends Animal {
private breed: string;
constructor(name: string, age: number, breed: string) {
super(name, age); // 调用父类的constructor
this.breed = breed;
}
// 重写父类方法
speak() {
console.log(`${this.name} barks!`);
}
// 子类特有方法
fetch() {
console.log(`${this.name} is fetching the ball.`);
}
getInfo() {
return `${this.name} is a ${this.breed} and ${this.age} years old.`;
}
}
// 使用示例
const myDog = new Dog("Buddy", 3, "Golden Retriever");
myDog.speak(); // 输出: Buddy barks!
myDog.sleep(); // 输出: Buddy is sleeping.
myDog.fetch(); // 输出: Buddy is fetching the ball.
console.log(myDog.getInfo());
3. 抽象类:定义框架
抽象类就像建筑的设计规范,它定义了结构但不提供完整实现。
typescript
abstract class Shape {
constructor(public name: string) {}
// 抽象方法,必须在子类中实现
abstract calculateArea(): number;
// 具体方法
displayInfo() {
console.log(`Shape: ${this.name}, Area: ${this.calculateArea()}`);
}
}
class Circle extends Shape {
constructor(private radius: number) {
super("Circle");
}
calculateArea(): number {
return Math.PI * this.radius * this.radius;
}
}
class Rectangle extends Shape {
constructor(private width: number, private height: number) {
super("Rectangle");
}
calculateArea(): number {
return this.width * this.height;
}
}
// 使用示例
const circle = new Circle(5);
const rectangle = new Rectangle(4, 6);
circle.displayInfo(); // 输出: Shape: Circle, Area: 78.53981633974483
rectangle.displayInfo(); // 输出: Shape: Rectangle, Area: 24
实战场景:类在项目中的应用
场景1:数据模型层
在我的电商项目中,类帮助我们构建了清晰的数据模型:
typescript
class Product {
constructor(
public id: number,
public name: string,
public price: number,
private stock: number
) {}
// 业务方法
reduceStock(quantity: number): boolean {
if (this.stock >= quantity) {
this.stock -= quantity;
return true;
}
return false;
}
addStock(quantity: number): void {
this.stock += quantity;
}
getStock(): number {
return this.stock;
}
// 静态方法
static createFromData(data: any): Product {
return new Product(data.id, data.name, data.price, data.stock);
}
}
class ShoppingCart {
private items: Map<number, { product: Product; quantity: number }> = new Map();
addItem(product: Product, quantity: number): void {
if (product.reduceStock(quantity)) {
const existingItem = this.items.get(product.id);
if (existingItem) {
existingItem.quantity += quantity;
} else {
this.items.set(product.id, { product, quantity });
}
}
}
removeItem(productId: number): void {
const item = this.items.get(productId);
if (item) {
item.product.addStock(item.quantity);
this.items.delete(productId);
}
}
getTotal(): number {
let total = 0;
for (const [_, item] of this.items) {
total += item.product.price * item.quantity;
}
return total;
}
checkout(): Order {
return new Order(this);
}
}
class Order {
private orderId: string;
private orderDate: Date;
constructor(private cart: ShoppingCart) {
this.orderId = this.generateOrderId();
this.orderDate = new Date();
}
private generateOrderId(): string {
return `ORD-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
}
场景2:UI组件系统
在React项目中,类组件虽然现在较少使用,但在某些场景下仍然很有价值:
typescript
abstract class BaseComponent<P = {}, S = {}> {
protected state: S;
protected props: P;
constructor(props: P) {
this.props = props;
this.state = {} as S;
}
abstract render(): string;
setState(newState: Partial<S>): void {
this.state = { ...this.state, ...newState };
this.onStateUpdate();
}
protected onStateUpdate(): void {
// 触发重新渲染的逻辑
console.log("State updated, triggering re-render");
}
}
class CounterComponent extends BaseComponent<{ initialCount: number }, { count: number }> {
constructor(props: { initialCount: number }) {
super(props);
this.state = { count: props.initialCount };
}
increment = (): void => {
this.setState({ count: this.state.count + 1 });
};
decrement = (): void => {
this.setState({ count: this.state.count - 1 });
};
render(): string {
return `
<div class="counter">
<h2>Count: ${this.state.count}</h2>
<button onclick="counter.increment()">+</button>
<button onclick="counter.decrement()">-</button>
</div>
`;
}
}
// 使用示例
const counter = new CounterComponent({ initialCount: 0 });
console.log(counter.render());
场景3:服务层和工具类
在服务层设计中,类提供了很好的封装:
typescript
class ApiService {
private baseURL: string;
private token: string | null = null;
constructor(baseURL: string) {
this.baseURL = baseURL;
}
setToken(token: string): void {
this.token = token;
}
private async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
const url = `${this.baseURL}${endpoint}`;
const headers: HeadersInit = {
'Content-Type': 'application/json',
...options.headers,
};
if (this.token) {
headers['Authorization'] = `Bearer ${this.token}`;
}
try {
const response = await fetch(url, {
...options,
headers,
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('API request failed:', error);
throw error;
}
}
async get<T>(endpoint: string): Promise<T> {
return this.request<T>(endpoint);
}
async post<T>(endpoint: string, data: any): Promise<T> {
return this.request<T>(endpoint, {
method: 'POST',
body: JSON.stringify(data),
});
}
}
// 单例模式的应用
class ConfigManager {
private static instance: ConfigManager;
private config: Map<string, any> = new Map();
private constructor() {
// 私有构造函数,防止外部实例化
}
static getInstance(): ConfigManager {
if (!ConfigManager.instance) {
ConfigManager.instance = new ConfigManager();
}
return ConfigManager.instance;
}
set(key: string, value: any): void {
this.config.set(key, value);
}
get(key: string): any {
return this.config.get(key);
}
}
最佳实践和注意事项
1. 组合优于继承
typescript
// 不推荐:过度使用继承
class AnimalWithSwim extends Animal {
swim() { /* ... */ }
}
class AnimalWithFly extends Animal {
fly() { /* ... */ }
}
class AnimalWithBoth extends Animal {
swim() { /* ... */ }
fly() { /* ... */ }
}
// 推荐:使用组合
class Swimmer {
swim() {
console.log("Swimming...");
}
}
class Flyer {
fly() {
console.log("Flying...");
}
}
class Duck extends Animal {
private swimmer = new Swimmer();
private flyer = new Flyer();
swim() {
this.swimmer.swim();
}
fly() {
this.flyer.fly();
}
}
2. 合理使用Getter和Setter
typescript
class User {
private _email: string = "";
constructor(public name: string) {}
get email(): string {
return this._email;
}
set email(value: string) {
if (this.isValidEmail(value)) {
this._email = value;
} else {
throw new Error("Invalid email address");
}
}
private isValidEmail(email: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+.[^\s@]+$/;
return emailRegex.test(email);
}
}
const user = new User("Alice");
user.email = "alice@example.com"; // ✅ 有效
// user.email = "invalid-email"; // ❌ 抛出错误
3. 接口与类的结合
typescript
interface IRepository<T> {
findById(id: number): T | null;
save(entity: T): void;
delete(id: number): boolean;
}
class UserRepository implements IRepository<User> {
private users: Map<number, User> = new Map();
private nextId: number = 1;
findById(id: number): User | null {
return this.users.get(id) || null;
}
save(user: User): void {
if (!user.id) {
user.id = this.nextId++;
}
this.users.set(user.id, user);
}
delete(id: number): boolean {
return this.users.delete(id);
}
}
结语
TypeScript的类为我们提供了强大的面向对象编程能力,从简单的数据封装到复杂的系统架构,类都能发挥重要作用。通过合理的类设计,我们可以创建出更加健壮、可维护的应用程序。
记住,类的设计不是越多越好,而是要找到适合项目需求的平衡点。希望今天的分享能帮助你在实际项目中更好地运用TypeScript类!
⭐ 写在最后
请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.
✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式
✅ 认为我部分代码过于老旧,可以提供新的API或最新语法
✅ 对于文章中部分内容不理解
✅ 解答我文章中一些疑问
✅ 认为某些交互,功能需要优化,发现BUG
✅ 想要添加新功能,对于整体的设计,外观有更好的建议
✅ 一起探讨技术加qq交流群:906392632
最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!