JS TS将装饰器应用于所有方法/枚举类方法

Chr*_*ris 4 javascript decorator typescript

我想将装饰器函数应用于类中的所有方法,以便可以替换:

class User {
    @log
    delete() {}

    @log
    create() {}

    @log
    update() {}
}
Run Code Online (Sandbox Code Playgroud)

@log
class User {
    delete() {}
    create() {}
    update() {}
}
Run Code Online (Sandbox Code Playgroud)

Pap*_*och 12

对于将来偶然发现这一点的人:

我从大卫的回答中获得灵感并创建了我自己的版本。后来我把它做成了一个npm包: https: //www.npmjs.com/package/decorate-all

在OP的场景中,会这样使用

@DecorateAll(log)
class User {
    delete() {}
    create() {}
    update() {}
}
Run Code Online (Sandbox Code Playgroud)


Dav*_*ret 7

创建一个类装饰器,并枚举目标原型上的属性。

对于每个属性:

  1. 获取属性描述符。
  2. 确保它是一种方法。
  3. 将描述符值包装在一个新函数中,该函数记录有关方法调用的信息。
  4. 重新定义修改后的属性描述符回到属性。

修改属性描述符很重要,因为您要确保装饰器可以与其他修改属性描述符的装饰器一起正常工作。

function log(target: Function) {
    for (const propertyName of Object.keys(target.prototype)) {
        const descriptor = Object.getOwnPropertyDescriptor(target.prototype, propertyName);
        const isMethod = descriptor.value instanceof Function;
        if (!isMethod)
            continue;

        const originalMethod = descriptor.value;
        descriptor.value = function (...args: any[]) {
            console.log("The method args are: " + JSON.stringify(args));
            const result = originalMethod.apply(this, args);
            console.log("The return value is: " + result);
            return result;
        };

        Object.defineProperty(target.prototype, propertyName, descriptor);        
    }
}
Run Code Online (Sandbox Code Playgroud)

基类方法

如果您希望这也影响基类方法,则可能需要遵循以下几条原则:

function log(target: Function) {
    for (const propertyName in target.prototype) {
        const propertyValue = target.prototype[propertyName];
        const isMethod = propertyValue instanceof Function;
        if (!isMethod)
            continue;

        const descriptor = getMethodDescriptor(propertyName);
        const originalMethod = descriptor.value;
        descriptor.value = function (...args: any[]) {
            console.log("The method args are: " + JSON.stringify(args));
            const result = originalMethod.apply(this, args);
            console.log("The return value is: " + result);
            return result;
        };

        Object.defineProperty(target.prototype, propertyName, descriptor);        
    }

    function getMethodDescriptor(propertyName: string): TypedPropertyDescriptor<any> {
        if (target.prototype.hasOwnProperty(propertyName))
            return Object.getOwnPropertyDescriptor(target.prototype, propertyName);

        // create a new property descriptor for the base class' method 
        return {
            configurable: true,
            enumerable: true,
            writable: true,
            value: target.prototype[propertyName]
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 当编译目标不是 ES5 时,Object.keys 不起作用。Object.getOwnPropertyNames(target.prototype) 对我来说对于其他所有目标都工作得很好。 (3认同)