MST

星途 面试题库

面试题:TypeScript装饰器在大型项目架构中的深度应用

请深入探讨TypeScript装饰器的原理、创建方法及其在大型前端项目架构设计中的应用,比如如何利用装饰器实现权限控制、日志记录、依赖注入等功能,并且说明使用装饰器可能带来的潜在问题及解决方案。
10.8万 热度难度
前端开发TypeScript

知识考点

AI 面试

面试题答案

一键面试

TypeScript装饰器原理

  1. 本质:装饰器是一种特殊类型的声明,它能够被附加到类声明、方法、属性或参数上。本质上,它们是函数,在运行时对目标对象进行元编程。
  2. 执行时机:在类的声明和成员被评估时执行。例如,类装饰器在类定义被加载时执行,方法装饰器在方法定义时执行。

创建方法

  1. 类装饰器
function classDecorator(target: Function) {
    // 这里的target就是被装饰的类的构造函数
    console.log('Class Decorator:', target.name);
    return class extends target {
        newProperty = 'new property added by decorator';
        constructor(...args: any[]) {
            super(...args);
            console.log('New property added:', this.newProperty);
        }
    };
}

@classDecorator
class MyClass {}
  1. 方法装饰器
function methodDecorator(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    descriptor.value = function(...args: any[]) {
        console.log('Before method execution');
        const result = originalMethod.apply(this, args);
        console.log('After method execution');
        return result;
    };
    return descriptor;
}

class MyClass2 {
    @methodDecorator
    myMethod() {
        console.log('Method is running');
    }
}
  1. 属性装饰器
function propertyDecorator(target: Object, propertyKey: string) {
    let value: any;
    const getter = function() {
        return value;
    };
    const setter = function(newValue: any) {
        console.log('Setting property value:', newValue);
        value = newValue;
    };
    if (delete target[propertyKey]) {
        Object.defineProperty(target, propertyKey, {
            get: getter,
            set: setter,
            enumerable: true,
            configurable: true
        });
    }
}

class MyClass3 {
    @propertyDecorator
    myProperty: string;
}
  1. 参数装饰器
function parameterDecorator(target: Object, propertyKey: string, parameterIndex: number) {
    console.log(`Parameter decorator on ${propertyKey} at index ${parameterIndex}`);
}

class MyClass4 {
    myMethod(@parameterDecorator param: string) {
        console.log('Method with parameter:', param);
    }
}

在大型前端项目架构设计中的应用

  1. 权限控制
function requirePermission(permission: string) {
    return function(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
        const originalMethod = descriptor.value;
        descriptor.value = function(...args: any[]) {
            // 这里模拟权限检查逻辑
            const hasPermission = checkPermission(permission);
            if (hasPermission) {
                return originalMethod.apply(this, args);
            } else {
                throw new Error('Permission denied');
            }
        };
        return descriptor;
    };
}

function checkPermission(permission: string): boolean {
    // 实际逻辑中从存储或API获取权限信息
    return true;
}

class AdminPanel {
    @requirePermission('admin:view')
    viewDashboard() {
        console.log('Viewing dashboard');
    }
}
  1. 日志记录
function log(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    descriptor.value = function(...args: any[]) {
        console.log(`Calling method ${propertyKey} with args:`, args);
        const result = originalMethod.apply(this, args);
        console.log(`Method ${propertyKey} returned:`, result);
        return result;
    };
    return descriptor;
}

class Calculator {
    @log
    add(a: number, b: number) {
        return a + b;
    }
}
  1. 依赖注入
const inject = <T>(dependency: T) => (target: Object, propertyKey: string) => {
    Object.defineProperty(target, propertyKey, {
        value: dependency,
        enumerable: true,
        configurable: true
    });
};

class DatabaseService {
    connect() {
        console.log('Connected to database');
    }
}

class UserService {
    @inject(new DatabaseService())
    database: DatabaseService;

    getUser() {
        this.database.connect();
        console.log('Getting user');
    }
}

潜在问题及解决方案

  1. 兼容性问题:装饰器是ES7的提案,目前在一些环境中可能不支持。
    • 解决方案:使用Babel等工具进行转译,将装饰器代码转换为ES5或ES6代码,以确保广泛的兼容性。
  2. 性能问题:由于装饰器在运行时执行,可能会影响性能,特别是在频繁调用的方法上使用装饰器。
    • 解决方案:尽量避免在性能敏感的代码路径上使用装饰器。或者对装饰器逻辑进行优化,减少不必要的计算。
  3. 调试困难:装饰器的嵌套和复杂逻辑可能导致调试困难。
    • 解决方案:在装饰器内部添加详细的日志记录,使用调试工具(如Chrome DevTools)来跟踪装饰器的执行流程。同时,尽量保持装饰器逻辑简单,易于理解和调试。