一、装饰器在TypeScript中的基础概念

装饰器是一种特殊的声明,它可以被附加到类、方法、属性或参数上。在TypeScript中,装饰器是一种元编程的工具,允许我们在运行时对代码进行额外的处理。

1.1 装饰器的基本语法

装饰器使用@符号加上装饰器函数的名称来表示。例如:

// 定义一个简单的装饰器函数
function myDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    // 这里可以对目标进行一些操作
    console.log(`装饰器被应用到 ${propertyKey}`);
}

class MyClass {
    @myDecorator
    myMethod() {
        console.log('这是我的方法');
    }
}

let myObj = new MyClass();
myObj.myMethod();

在这个例子中,myDecorator是一个装饰器函数,它被应用到MyClass类的myMethod方法上。当调用myMethod时,装饰器函数中的代码也会被执行。

二、装饰器的功能

2.1 方法装饰器的功能

方法装饰器可以用于修改方法的行为。例如,我们可以在方法调用前后添加日志记录:

// 定义一个日志装饰器
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    descriptor.value = function (...args: any[]) {
        console.log(`调用方法 ${propertyKey} 之前`);
        const result = originalMethod.apply(this, args);
        console.log(`调用方法 ${propertyKey} 之后`);
        return result;
    };
    return descriptor;
}

class Calculator {
    @log
    add(a: number, b: number) {
        return a + b;
    }
}

let calculator = new Calculator();
let result = calculator.add(2, 3);

在这个例子中,log装饰器在add方法调用前后打印日志。

2.2 属性装饰器的功能

属性装饰器可以用于控制属性的访问。例如,我们可以创建一个只读属性装饰器:

// 定义一个只读属性装饰器
function readonly(target: any, propertyKey: string) {
    let value;
    Object.defineProperty(target, propertyKey, {
        get() {
            return value;
        },
        set(newValue) {
            console.log('尝试修改只读属性');
        },
        configurable: true,
        enumerable: true
    });
    return;
}

class Person {
    @readonly
    name: string = 'John';
}

let person = new Person();
console.log(person.name);
person.name = 'Jane';

在这个例子中,readonly装饰器使得name属性变为只读。

三、装饰器的使用场景

3.1 日志记录

在开发过程中,日志记录是非常重要的。装饰器可以方便地在方法调用前后添加日志,而不需要修改方法的核心逻辑。

// 日志装饰器
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    descriptor.value = function (...args: any[]) {
        console.log(`调用方法 ${propertyKey} 之前,参数: ${args}`);
        const result = originalMethod.apply(this, args);
        console.log(`调用方法 ${propertyKey} 之后,结果: ${result}`);
        return result;
    };
    return descriptor;
}

class OrderService {
    @log
    placeOrder(order: any) {
        // 这里是下单的具体逻辑
        return `订单已提交: ${JSON.stringify(order)}`;
    }
}

let orderService = new OrderService();
let order = { id : 1, items : ['苹果', '香蕉'] };
let result = orderService.placeOrder(order);

3.2 权限控制

在一些应用中,需要对某些方法或属性进行权限控制。装饰器可以用于检查用户是否具有相应的权限。

// 权限控制装饰器
function checkPermission(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    descriptor.value = function (...args: any[]) {
        const user = { role : 'admin' }; // 这里假设用户信息
        if (user.role === 'admin') {
            return originalMethod.apply(this, args);
        } else {
            console.log('没有权限访问此方法');
        }
    };
    return descriptor;
}

class AdminPanel {
    @checkPermission
    deleteUser(userId: number) {
        // 这里是删除用户的逻辑
        return `用户 ${userId} 已被删除`;
    }
}

let adminPanel = new AdminPanel();
let resultDelete = adminPanel.deleteUser(1);

3.3 缓存

对于一些计算成本较高的方法,我们可以使用装饰器来实现缓存机制。

// 缓存装饰器
function cache(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    const cache = new Map();
    descriptor.value = function (...args: any[]) {
        const key = JSON.stringify(args);
        if (cache.has(key)) {
            return cache.get(key);
        }
        const result = originalMethod.apply(this, args);
        cache.set(key, result);
        return result;
    };
    return descriptor;
}

class MathUtils {
    @cache
    factorial(n: number) {
        if (n === 0) {
            return 1;
        }
        return n * this.factorial(n - 1);
    }
}

let mathUtils = new MathUtils();
let resultFactorial1 = mathUtils.factorial(5);
let resultFactorial2 = mathUtils.factorial(5);

四、装饰器的技术优缺点

4.1 优点

  • 代码复用性高:装饰器可以被多个类或方法复用,减少了重复代码。
  • 增强代码的可读性和可维护性:将一些横切关注点(如日志、权限控制)从核心业务逻辑中分离出来,使得代码更加清晰。
  • 便于扩展:可以很方便地添加新的装饰器来满足不同的需求。

4.2 缺点

  • 可能导致性能问题:如果装饰器中包含复杂的逻辑,可能会影响方法的执行效率。
  • 调试困难:由于装饰器的执行顺序和机制,调试时可能会比较复杂。

五、使用装饰器的注意事项

5.1 装饰器的执行顺序

在TypeScript中,装饰器的执行顺序是从下往上,从左往右。这意味着在一个方法或属性上有多个装饰器时,最下面的装饰器会先执行。

5.2 装饰器与类的继承

当一个类继承另一个类时,装饰器不会被继承。如果需要在子类中使用相同的装饰器,需要重新应用。

六、文章总结

装饰器在TypeScript中是一种非常强大的工具,它可以用于实现多种功能,如日志记录、权限控制和缓存等。通过合理使用装饰器,可以提高代码的质量和可维护性。然而,在使用装饰器时,需要注意其优缺点和执行顺序等问题。希望通过本文的介绍,读者能够更好地理解和应用装饰器。