MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

TypeScript类装饰器高级用法:工厂函数与多装饰器组合

2021-12-044.4k 阅读

TypeScript类装饰器基础回顾

在深入探讨TypeScript类装饰器的高级用法之前,让我们先回顾一下类装饰器的基础知识。类装饰器是一种在TypeScript中非常强大的元编程工具,它允许我们在类定义时对类进行修改。

类装饰器的基本语法如下:

function classDecorator(target: Function) {
    // 在这里对类进行修改
    console.log('类装饰器被调用,目标类:', target);
}

@classDecorator
class MyClass {
    constructor() {
        console.log('MyClass 实例化');
    }
}

在上述代码中,classDecorator是一个类装饰器函数,它接受一个参数target,这个target就是被装饰的类的构造函数。当MyClass类定义时,classDecorator会被调用,并且target就是MyClass的构造函数。

类装饰器工厂函数

什么是类装饰器工厂函数

类装饰器工厂函数是一个返回类装饰器的函数。它的主要作用是提供一种灵活的方式来定制类装饰器的行为。通过类装饰器工厂函数,我们可以在调用装饰器时传递参数,从而根据不同的参数对类进行不同的修改。

类装饰器工厂函数的语法

下面是一个简单的类装饰器工厂函数的示例:

function classDecoratorFactory(param: string) {
    return function (target: Function) {
        console.log(`使用参数 ${param} 装饰类 ${target.name}`);
    };
}

@classDecoratorFactory('example param')
class AnotherClass {
    constructor() {
        console.log('AnotherClass 实例化');
    }
}

在上述代码中,classDecoratorFactory是一个类装饰器工厂函数,它接受一个参数param。这个工厂函数返回一个真正的类装饰器函数,该函数接受target参数,即被装饰的类的构造函数。当AnotherClass类定义时,classDecoratorFactory('example param')会被调用,返回的类装饰器函数会对AnotherClass进行装饰。

类装饰器工厂函数的实际应用

  1. 日志记录 假设我们希望在类实例化时记录不同级别的日志,我们可以使用类装饰器工厂函数来实现:
function logLevelDecoratorFactory(level: 'info' | 'warning' | 'error') {
    return function (target: Function) {
        const originalConstructor = target;
        target = function (...args: any[]) {
            let logMessage = `实例化 ${originalConstructor.name}`;
            if (level === 'info') {
                console.info(logMessage);
            } else if (level === 'warning') {
                console.warn(logMessage);
            } else if (level === 'error') {
                console.error(logMessage);
            }
            return new originalConstructor(...args);
        } as any;
        return target;
    };
}

@logLevelDecoratorFactory('warning')
class LoggerClass {
    constructor() {
        console.log('LoggerClass 实例化');
    }
}

new LoggerClass();

在上述代码中,logLevelDecoratorFactory是一个类装饰器工厂函数,它接受一个level参数,用于指定日志级别。返回的类装饰器函数会在类实例化时根据level记录相应级别的日志。

  1. 依赖注入 类装饰器工厂函数在依赖注入方面也非常有用。假设我们有一个服务类,并且希望根据不同的环境注入不同的实现:
interface DatabaseService {
    connect(): void;
}

class ProductionDatabaseService implements DatabaseService {
    connect() {
        console.log('连接到生产数据库');
    }
}

class DevelopmentDatabaseService implements DatabaseService {
    connect() {
        console.log('连接到开发数据库');
    }
}

function databaseServiceInjectorFactory(env: 'production' | 'development') {
    return function (target: Function) {
        let databaseService: DatabaseService;
        if (env === 'production') {
            databaseService = new ProductionDatabaseService();
        } else {
            databaseService = new DevelopmentDatabaseService();
        }
        target.prototype.getDatabaseService = function () {
            return databaseService;
        };
    };
}

@databaseServiceInjectorFactory('development')
class App {
    constructor() {
        const service = this.getDatabaseService();
        service.connect();
    }
    getDatabaseService(): DatabaseService {
        return {} as DatabaseService;
    }
}

new App();

在上述代码中,databaseServiceInjectorFactory是一个类装饰器工厂函数,它根据env参数决定注入哪个数据库服务实现。返回的类装饰器函数会在类的原型上添加一个getDatabaseService方法,用于获取注入的数据库服务实例。

多装饰器组合

多装饰器组合的概念

多装饰器组合是指在一个类上应用多个类装饰器。这些装饰器会按照从下到上(或者说从内到外,取决于你的理解方式)的顺序依次执行。每个装饰器都可以对类进行不同的修改,通过组合多个装饰器,我们可以实现更复杂的功能。

多装饰器组合的语法

下面是一个简单的多装饰器组合的示例:

function firstDecorator(target: Function) {
    console.log('第一个装饰器被调用,目标类:', target);
}

function secondDecorator(target: Function) {
    console.log('第二个装饰器被调用,目标类:', target);
}

@firstDecorator
@secondDecorator
class MultiDecoratedClass {
    constructor() {
        console.log('MultiDecoratedClass 实例化');
    }
}

在上述代码中,MultiDecoratedClass类应用了两个装饰器firstDecoratorsecondDecorator。当类定义时,secondDecorator会首先被调用,然后是firstDecorator

多装饰器组合的实际应用

  1. 安全与日志记录组合 假设我们有一个需要进行权限验证并且记录操作日志的类。我们可以使用两个装饰器来实现这个功能:
function authDecorator(target: Function) {
    const originalMethod = target.prototype.doSomething;
    target.prototype.doSomething = function () {
        console.log('进行权限验证');
        // 这里可以添加实际的权限验证逻辑
        originalMethod.apply(this, arguments);
    };
}

function logDecorator(target: Function) {
    const originalMethod = target.prototype.doSomething;
    target.prototype.doSomething = function () {
        console.log('记录操作日志');
        originalMethod.apply(this, arguments);
    };
}

@authDecorator
@logDecorator
class SecureAndLoggedClass {
    doSomething() {
        console.log('执行实际操作');
    }
}

const instance = new SecureAndLoggedClass();
instance.doSomething();

在上述代码中,SecureAndLoggedClass类应用了authDecoratorlogDecorator两个装饰器。当doSomething方法被调用时,首先会进行权限验证(authDecorator的逻辑),然后记录操作日志(logDecorator的逻辑),最后执行实际的操作。

  1. 性能监测与缓存组合 对于一些性能敏感的类,我们可能希望监测方法的执行时间,并且对频繁调用的方法进行缓存。我们可以通过组合两个装饰器来实现:
function performanceMonitorDecorator(target: Function) {
    const originalMethod = target.prototype.computeValue;
    target.prototype.computeValue = function () {
        const startTime = Date.now();
        const result = originalMethod.apply(this, arguments);
        const endTime = Date.now();
        console.log(`方法执行时间:${endTime - startTime} 毫秒`);
        return result;
    };
}

function cacheDecorator(target: Function) {
    const cache = new Map();
    const originalMethod = target.prototype.computeValue;
    target.prototype.computeValue = function () {
        const key = JSON.stringify(arguments);
        if (cache.has(key)) {
            return cache.get(key);
        }
        const result = originalMethod.apply(this, arguments);
        cache.set(key, result);
        return result;
    };
}

@performanceMonitorDecorator
@cacheDecorator
class ComputationClass {
    computeValue(a: number, b: number) {
        // 模拟复杂计算
        let sum = 0;
        for (let i = 0; i < 1000000; i++) {
            sum += a + b;
        }
        return sum;
    }
}

const compInstance = new ComputationClass();
compInstance.computeValue(1, 2);
compInstance.computeValue(1, 2);

在上述代码中,ComputationClass类应用了performanceMonitorDecoratorcacheDecorator两个装饰器。cacheDecorator会对computeValue方法的调用进行缓存,performanceMonitorDecorator会监测方法的执行时间。当computeValue方法被多次调用时,第二次调用会从缓存中获取结果,并且两次调用都会记录执行时间。

多装饰器组合与类装饰器工厂函数的结合

结合的方式

将多装饰器组合与类装饰器工厂函数结合可以进一步增强代码的灵活性和可维护性。我们可以使用类装饰器工厂函数生成不同配置的装饰器,然后将这些装饰器组合应用到类上。

实际应用示例

假设我们有一个电商应用,对于不同类型的产品(如电子产品、服装等),我们需要进行不同级别的库存检查和不同类型的促销活动。我们可以通过类装饰器工厂函数和多装饰器组合来实现:

function inventoryCheckDecoratorFactory(productType: 'electronics' | 'clothing') {
    return function (target: Function) {
        const originalMethod = target.prototype.sellProduct;
        target.prototype.sellProduct = function () {
            if (productType === 'electronics') {
                console.log('检查电子产品库存');
                // 实际的电子产品库存检查逻辑
            } else {
                console.log('检查服装库存');
                // 实际的服装库存检查逻辑
            }
            originalMethod.apply(this, arguments);
        };
    };
}

function promotionDecoratorFactory(promotionType: 'discount' | 'bundle') {
    return function (target: Function) {
        const originalMethod = target.prototype.sellProduct;
        target.prototype.sellProduct = function () {
            if (promotionType === 'discount') {
                console.log('应用折扣促销');
                // 实际的折扣促销逻辑
            } else {
                console.log('应用捆绑促销');
                // 实际的捆绑促销逻辑
            }
            originalMethod.apply(this, arguments);
        };
    };
}

@inventoryCheckDecoratorFactory('electronics')
@promotionDecoratorFactory('discount')
class ElectronicsProduct {
    sellProduct() {
        console.log('销售电子产品');
    }
}

@inventoryCheckDecoratorFactory('clothing')
@promotionDecoratorFactory('bundle')
class ClothingProduct {
    sellProduct() {
        console.log('销售服装');
    }
}

const electronics = new ElectronicsProduct();
electronics.sellProduct();

const clothing = new ClothingProduct();
clothing.sellProduct();

在上述代码中,inventoryCheckDecoratorFactorypromotionDecoratorFactory是两个类装饰器工厂函数。ElectronicsProduct类应用了针对电子产品的库存检查装饰器和折扣促销装饰器,ClothingProduct类应用了针对服装的库存检查装饰器和捆绑促销装饰器。通过这种方式,我们可以根据不同的产品类型灵活地组合不同的装饰器功能。

注意事项

  1. 装饰器执行顺序 在多装饰器组合时,要注意装饰器的执行顺序。从下到上(或者从内到外)的顺序执行可能会影响最终的效果。例如,如果一个装饰器修改了类的原型,另一个装饰器基于原型进行操作,那么执行顺序就非常关键。

  2. 装饰器的副作用 每个装饰器都可能对类产生副作用,例如修改类的原型、添加属性或方法等。在组合多个装饰器时,要确保这些副作用不会相互冲突。例如,两个装饰器都试图在类的原型上添加同名的方法,就会导致覆盖问题。

  3. 类装饰器工厂函数的参数验证 在类装饰器工厂函数中,要对传入的参数进行充分的验证。如果参数无效,可能会导致装饰器行为异常。例如,在logLevelDecoratorFactory中,如果传入的level参数不是'info''warning''error'中的一个,就需要进行适当的处理,比如抛出错误或者默认设置为'info'

  4. 与其他编程模式的结合 类装饰器虽然强大,但也应该与其他编程模式(如依赖注入、模块模式等)结合使用,以实现更完整、可维护的应用程序架构。例如,在依赖注入的场景中,类装饰器可以作为一种便捷的方式来注入依赖,但还需要结合模块系统来管理依赖的生命周期。

通过深入理解和灵活运用TypeScript类装饰器的工厂函数与多装饰器组合,我们可以在前端开发中实现更强大、灵活和可维护的代码结构。无论是日志记录、权限验证、性能监测还是其他各种功能,这些高级用法都为我们提供了丰富的手段来优化和扩展我们的代码。同时,在使用过程中要注意遵循最佳实践,避免常见的问题,以确保代码的质量和稳定性。