zoukankan      html  css  js  c++  java
  • 【转】typescript class decorator装饰器

    原文:

    https://www.logicbig.com/tutorials/misc/typescript/class-decorators.html

    https://medium.com/iqoqo-engineering/understand-typescript-decorators-in-5-minutes-26ffc6189082

    ————————————————————————————————————————————————————————

    TypeScript - Class Decorators

    [Last Updated: Feb 28, 2019]

    What are decorators?

     A Decorator is a special kind of declaration that can be attached to a class declaration, method, accessor, property, or parameter. Decorators use the form @expression, where expression must evaluate to a function that will be called at runtime with information about the decorated declaration.

    Decorators are a stage 2 proposal for JavaScript. That means decorators will be part of JavaScript in a future release.
    Decorators are available as an experimental feature of TypeScript.

    To enable decorators, we must enable the experimentalDecorators compiler option either on the command line or in your tsconfig.json.

    Class decorators

    This tutorial shows the use of Class decorators.

    If a decorator expression has the arguments e.g. @expression("a", 3) then these arguments are passed to the decorator function expression(arg1: string, arg2: number).

    If a decorator expression has no arguments e.g. @expression then the constructor function of the class is passed to the decorator function expression(theConstructor: Function)

    In both above cases the decorator expression can return a new constructor function to replace or override the original constructor function.

    Examples

    tsconfig.json

    {
        "compilerOptions": {
            "target": "ES6",
            "experimentalDecorators": true
        }
    }

    In the presence of the above file, we simply need to run tsc to compile all .ts files to .js files in the same directory.

    Also check out tutorial on tsconfig.json.

    A Basic Decorator - adding new properties

    In following example, the decorator SelfDriving adds a new property to the target class.

    Ex1ClassDecorator.ts

    function SelfDriving(constructorFunction: Function) {
        console.log('-- decorator function invoked --');
        constructorFunction.prototype.selfDrivable = true;
    }
    
    @SelfDriving
    class Car {
        private _make: string;
        constructor(make: string) {
            console.log('-- this constructor invoked --');
            this._make = make;
        }
    }
    console.log('-- creating an instance --');
    let car: Car = new Car("Nissan");
    console.log(car);
    console.log(`selfDriving: ${car['selfDrivable']}`);
    
    console.log('-- creating one more instance --');
    car = new Car("Toyota");
    console.log(car);
    console.log(`selfDriving: ${car['selfDrivable']}`);

    Output

    -- decorator function invoked --
    -- creating an instance --
    -- this constructor invoked --
    Car { _make: 'Nissan' }
    selfDriving: true
    -- creating one more instance --
    -- this constructor invoked --
    Car { _make: 'Toyota' }
    selfDriving: true

    As seen above the decorator function is called only once when the class Car definition is loaded (before any instance is created).

    The compiled JavaScript

    var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
        var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
        if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
        else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
        return c > 3 && r && Object.defineProperty(target, key, r), r;
    };
    function SelfDriving(constructorFunction) {
        console.log('-- decorator function invoked --');
        constructorFunction.prototype.selfDrivable = true;
    }
    var Car = /** @class */ (function () {
        function Car(make) {
            console.log('-- this constructor invoked --');
            this._make = make;
        }
        Car = __decorate([
            SelfDriving
        ], Car);
        return Car;
    }());
    console.log('-- creating an instance --');
    var car = new Car("Nissan");
    console.log(car);
    console.log("selfDriving: " + car['selfDrivable']);
    console.log('-- creating one more instance --');
    car = new Car("Toyota");
    console.log(car);
    console.log("selfDriving: " + car['selfDrivable']);

    Decorator with parameter

    To pass parameters to a Decorator, a 'Decorator Factory' is used which simply returns a function that will be called by the decorator at runtime.

    function Wheels(numOfWheels: number) {
        console.log('-- decorator factory invoked --');
        return function (constructor: Function) {
            console.log('-- decorator invoked --');
            constructor.prototype.wheels = numOfWheels;
        }
    }
    
    @Wheels(4)
    class Vechical {
        private _make: string;
        constructor(make: string) {
            console.log('-- this constructor invoked --');
            this._make = make;
        }
    }
    
    console.log('-- creating an instance --');
    
    let vechical: Vechical = new Vechical("Nissan");
    console.log(vechical);
    
    console.log(vechical['wheels']);
    
    console.log('-- creating another instance --');
    
    vechical = new Vechical("Toyota");
    console.log(vechical);
    console.log(vechical['wheels']);

    Output

    -- decorator factory invoked --
    -- decorator invoked --
    -- creating an instance --
    -- this constructor invoked --
    Vechical { _make: 'Nissan' }
    4
    -- creating another instance --
    -- this constructor invoked --
    Vechical { _make: 'Toyota' }
    4

    In above example the method Wheels() is a decorator factory which returns a decorator function. The decorator function applies the desired logic based on the passed parameters and the constructor function.

    Extending Constructor

    Ex3OverrideConstructor.ts

    function Compact<T extends { new(...args: any[]): {} }>(constructor: T) {
        console.log("-- decorator function invoked --");
        return class extends constructor {
            gears: number = 5;
            wheels: number = 3;
        }
    }
    
    @Compact
    class Automobile {
        make: string;
        wheels: number = 4;
        constructor(make: string) {
            console.log("-- this constructor invoked --");
            this.make = make;
        }
    }
    
    console.log("-- creating an instance --");
    console.log(new Automobile("Nissan"));
    console.log("-- creating another instance --");
    console.log(new Automobile("Toyota"));

    Output

    -- decorator function invoked --
    -- creating an instance --
    -- this constructor invoked --
    class_1 { wheels: 3, make: 'Nissan', gears: 5 }
    -- creating another instance --
    -- this constructor invoked --
    class_1 { wheels: 3, make: 'Toyota', gears: 5 }

    Also check out tutorials on generic constraints with new() and JavaScript class expressions.

    Extending constructor with parameters

    Ex4OverrideConstructor.ts

    function Specs(numGears: number, numWheels: number) {
        return function <T extends { new(...args: any[]): {} }>(constructor: T) {
            return class extends constructor {
                gears = numGears;
                wheels = numWheels;
            }
        }
    }
    
    @Specs(3, 4)
    class Wagon {
        make: string;
        constructor(make: string) {
            this.make = make;
        }
    }
    
    console.log(new Wagon("Nissan"));

    Output

    Wagon { make: 'Nissan', gears: 3, wheels: 4 }

    Wrapping constructor

    In following example we are going to wrap the original constructor in a new constructor.

    Ex5ConstructorInterceptor.ts

    function log<T extends { new(...constructorArgs: any[]) }>(constructorFunction: T) {
    
        //new constructor function
        let newConstructorFunction: any = function (...args) {
            console.log("before invoking: " + constructorFunction.name);
            let func: any = function () {
                return new constructorFunction(...args);
            }
            func.prototype = constructorFunction.prototype;
            let result: any = new func();
            console.log("after invoking: " + constructorFunction.name);
            console.log('object created: ' + JSON.stringify(result));
            return result;
        }
        newConstructorFunction.prototype = constructorFunction.prototype;
        return newConstructorFunction;
    }
    
    @log
    class Task {
        taskName: string;
        constructor(taskName: string) {
            console.log('this constructor invoked');
            this.taskName = taskName;
        }
    }
    console.log("creating an instance");
    
    let task = new Task("test");
    console.log('task created: ' + JSON.stringify(task));
    console.log("instanceof Task: " + (task instanceof Task));

    Output

    creating an instance
    before invoking: Task
    this constructor invoked
    after invoking: Task
    object created: {"taskName":"test"}
    task created: {"taskName":"test"}
    instanceof Task: true

    Wrapping constructor with parameters

    Ex6ConstructorInterceptor.ts

    function Listener<I extends ObjectListener<any>>(listener: I) {
    
        return function <T extends { new(...constructorArgs: any[]) }>(constructorFunction: T) {
            //new constructor function
            let newConstructorFunction: any = function (...args) {
                let func: any = function () {
                    return new constructorFunction(...args);
                }
                func.prototype = constructorFunction.prototype;
                let result: any = new func();
                listener.onObjectCreation(result);
                return result;
            }
            newConstructorFunction.prototype = constructorFunction.prototype;
            return newConstructorFunction;
        }
    }
    
    interface ObjectListener<T> {
        onObjectCreation(t: T): void;
    
    }
    class MyObjectListener implements ObjectListener<any>{
        onObjectCreation(obj: any) {
            console.log("Object created: " + JSON.stringify(obj));
        }
    }
    
    @Listener(new MyObjectListener())
    class TaskRunner {
        taskName: string;
        constructor(taskName: string) {
            this.taskName = taskName;
        }
    }
    console.log("creating an instance");
    
    let taskRunner = new TaskRunner("test");

    Output

    creating an instance
    Object created: {"taskName":"test"}
  • 相关阅读:
    用户、角色、权限管理-设计方案之权限检测
    供电绘图计算软件-新增了图库管理功能
    AutoCAD使用技巧六则
    环境影像评价系统
    AutoCAD.net: 如何实现裁剪功能Trim
    AutoCAD 命令参考手册
    arx常用的一些函数功能表
    AutoCAD.net: Curve.GetSplitCurves的用法
    AutoCAD.net:有条件选择AutoCAD实体
    电台节目管理软件
  • 原文地址:https://www.cnblogs.com/oxspirt/p/13729587.html
Copyright © 2011-2022 走看看