Typescript裝飾器淺析

介紹

隨着TypeScript和ES6裏引入了類,在一些場景下咱們須要額外的特性來支持標註或修改類及其成員。 裝飾器(Decorators)爲咱們在類的聲明及成員上經過元編程語法添加標註提供了一種方式。 Javascript裏的裝飾器目前處在 建議徵集的第二階段,但在TypeScript裏已作爲一項實驗性特性予以支持。html

使用

命令行:

tsc --target ES5 --experimentalDecorators

tsconfig.json:

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

裝飾器定義

裝飾器是一種特殊類型的聲明,它可以被附加到類聲明,方法, 訪問符,屬性或參數上。 裝飾器使用 @expression這種形式,expression求值後必須爲一個函數,它會在運行時被調用,被裝飾的聲明信息作爲參數傳入。shell

基礎使用

target指向的是HelloWordClass
function helloWord(target: any) {
    console.log('hello Word!');
}

@helloWord
class HelloWordClass {

}
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 helloWord(target) {
    console.log('hello Word!');
}
var HelloWordClass = /** @class */ (function () {
    function HelloWordClass() {
    }
    HelloWordClass = __decorate([
        helloWord
    ], HelloWordClass);
    return HelloWordClass;
}());

這是編譯事後的代碼,是聲明一個__decorate,把HelloWordClass傳給前面的fn數組

類裝飾器

應用於類構造函數,其參數是類的構造函數。express

function addAge(args: number) {
    return function (target: Function) {
        target.prototype.age = args;
    };
}

@addAge(18)
class Hello {
    name: string;
    age: number;
    constructor() {
        console.log('hello');
        this.name = 'yugo';
    }
}

console.log(Hello.prototype.age);//18
let hello = new Hello();

console.log(hello.age);//18

方法裝飾器

它會被應用到方法的 屬性描述符上,能夠用來監視,修改或者替換方法定義。
方法裝飾會在運行時傳入下列3個參數:編程

一、對於靜態成員來講是類的構造函數,對於實例成員是類的原型對象。
二、成員的名字。
三、成員的屬性描述符{value: any, writable: boolean, enumerable: boolean, configurable: boolean}。json

function addAge(constructor: Function) {
  constructor.prototype.age = 18;
}
​
function method(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
   console.log(target);
   console.log("prop " + propertyKey);
   console.log("desc " + JSON.stringify(descriptor) + "\n\n");
};
​
@addAge
class Hello{
  name: string;
  age: number;
  constructor() {
    console.log('hello');
    this.name = 'yugo';
  }
​
  @method
  hello(){
    return 'instance method';
  }
​
  @method
  static shello(){
    return 'static method';
  }
}

訪問器裝飾器

訪問器裝飾器應用於訪問器的屬性描述符,可用於觀察,修改或替換訪問者的定義。 訪問器裝飾器不能在聲明文件中使用,也不能在任何其餘環境上下文中使用(例如在聲明類中)。數組

class Point {
    private _x: number;
    private _y: number;
    constructor(x: number, y: number) {
        this._x = x;
        this._y = y;
    }

    @configurable(false)
    get x() { return this._x; }

    @configurable(false)
    get y() { return this._y; }
}

方法參數裝飾器

參數裝飾器表達式會在運行時看成函數被調用app

const parseConf = [];
class Modal {
    @parseFunc
    public addOne(@parse('number') num) {
        console.log('num:', num);
        return num + 1;
    }
}

// 在函數調用前執行格式化操做
function parseFunc(target, name, descriptor) {
    const originalMethod = descriptor.value;
    descriptor.value = function (...args: any[]) {
        for (let index = 0; index < parseConf.length; index++) {
            const type = parseConf[index];
            console.log(type);
            switch (type) {
                case 'number':
                    args[index] = Number(args[index]);
                    break;
                case 'string':
                    args[index] = String(args[index]);
                    break;
                case 'boolean':
                    args[index] = String(args[index]) === 'true';
                    break;
            }
            return originalMethod.apply(this, args);
        }
    };
    return descriptor;
}

// 向全局對象中添加對應的格式化信息
function parse(type) {
    return function (target, name, index) {
        parseConf[index] = type;
        console.log('parseConf[index]:', type);
    };
}
let modal = new Modal();
console.log(modal.addOne('10')); // 11

屬性裝飾器

屬性裝飾器表達式會在運行時看成函數被調用,函數

function log(target: any, propertyKey: string) {
    let value = target[propertyKey];
    // 用來替換的getter
    const getter = function () {
        console.log(`Getter for ${propertyKey} returned ${value}`);
        return value;
    }
    // 用來替換的setter
    const setter = function (newVal) {
        console.log(`Set ${propertyKey} to ${newVal}`);
        value = newVal;
    };
    // 替換屬性,先刪除原先的屬性,再從新定義屬性
    if (delete this[propertyKey]) {
        Object.defineProperty(target, propertyKey, {
            get: getter,
            set: setter,
            enumerable: true,
            configurable: true
        });
    }
}
class Calculator {
    @log
    public num: number;
    square() {
        return this.num * this.num;
    }
}
let cal = new Calculator();
cal.num = 2;
console.log(cal.square());
// Set num to 2
// Getter for num returned 2
// Getter for num returned 2
// 4
https://www.jianshu.com/p/afe...
https://www.tslang.cn/docs/ha...
相關文章
相關標籤/搜索