美文网首页
typescript 五种装饰器

typescript 五种装饰器

作者: 凡凡的小web | 来源:发表于2019-11-26 15:22 被阅读0次

装饰器类型

装饰器的类型有:类装饰器、访问器装饰器、属性装饰器、方法装饰器、参数装饰器,但是没有函数装饰器(function)。

1.类装饰器

应用于类构造函数,其参数是类的构造函数。
注意class并不是像Java那种强类型语言中的类,而是JavaScript构造函数的语法糖。

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);//18let hello = new Hello(); console.log(hello.age);//18

2.方法装饰器

它会被应用到方法的 属性描述符上,可以用来监视,修改或者替换方法定义。
方法装饰会在运行时传入下列3个参数:

  • 1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  • 2、成员的名字。
  • 3、成员的属性描述符{value: any, writable: boolean, enumerable: boolean, configurable: boolean}。
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");};​@addAgeclass Hello{  name: string;  age: number;  constructor() {    console.log('hello');    this.name = 'yugo';  }​  @method  hello(){    return 'instance method';  }​  @method  static shello(){    return 'static method';  }}

我们得到的结果是

Hello { hello: [Function] }prop hellodesc {"writable":true,"enumerable":true,"configurable":true}​​{ [Function: Hello] shello: [Function] }prop shellodesc {"writable":true,"enumerable":true,"configurable":true}

假如我们修饰的是 hello 这个实例方法,第一个参数将是原型对象,也就是 Hello.prototype。

假如是 shello 这个静态方法,则第一个参数是构造器 constructor。

第二个参数分别是属性名,第三个参数是属性修饰对象。

注意:在vscode编辑时有时会报作为表达式调用时,无法解析方法修饰器的签名。错误,此时需要在tsconfig.json中增加target配置项:

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

3. 访问器装饰器

访问器装饰器应用于访问器的属性描述符,可用于观察,修改或替换访问者的定义。 访问器装饰器不能在声明文件中使用,也不能在任何其他环境上下文中使用(例如在声明类中)。

注意: TypeScript不允许为单个成员装饰get和set访问器。相反,该成员的所有装饰器必须应用于按文档顺序指定的第一个访问器。这是因为装饰器适用于属性描述符,它结合了get和set访问器,而不是单独的每个声明。

访问器装饰器表达式会在运行时当作函数被调用,传入下列3个参数:

  • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  • 成员的名字。
  • 成员的属性描述符。

注意  如果代码输出目标版本小于ES5,Property Descriptor将会是undefined。

如果访问器装饰器返回一个值,它会被用作方法的属性描述符

注意  如果代码输出目标版本小于ES5返回值会被忽略。

下面是使用了访问器装饰器(@configurable)的例子,应用于Point类的成员上:

 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; }}

我们可以通过如下函数声明来定义@configurable装饰器:

function configurable(value: boolean) {    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {        descriptor.configurable = value;    };}

4. 方法参数装饰器

参数装饰器表达式会在运行时当作函数被调用,传入下列3个参数:

  • 1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  • 2、参数的名字。
  • 3、参数在函数参数列表中的索引。
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

5. 属性装饰器

属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数:

  • 1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  • 2、成员的名字。
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

装饰器加载顺序

image
function ClassDecorator() {    return function (target) {        console.log("I am class decorator");    }}function MethodDecorator() {    return function (target, methodName: string, descriptor: PropertyDescriptor) {        console.log("I am method decorator");    }}function Param1Decorator() {    return function (target, methodName: string, paramIndex: number) {        console.log("I am parameter1 decorator");    }}function Param2Decorator() {    return function (target, methodName: string, paramIndex: number) {        console.log("I am parameter2 decorator");    }}function PropertyDecorator() {    return function (target, propertyName: string) {        console.log("I am property decorator");    }} @ClassDecorator()class Hello {    @PropertyDecorator()    greeting: string;      @MethodDecorator()    greet( @Param1Decorator() p1: string, @Param2Decorator() p2: string) { }}

输出结果:

I am property decoratorI am parameter2 decoratorI am parameter1 decoratorI am method decoratorI am class decorator

从上述例子得出如下结论:

  1. 有多个参数装饰器时:从最后一个参数依次向前执行

  2. 方法和方法参数中参数装饰器先执行。

  3. 类装饰器总是最后执行。

  4. 方法和属性装饰器,谁在前面谁先执行。因为参数属于方法一部分,所以参数会一直紧紧挨着方法执行。

上述例子中属性和方法调换位置,输出如下结果:

I am parameter2 decoratorI am parameter1 decoratorI am method decoratorI am property decoratorI am class decorator

原文
https://blog.csdn.net/zdhsoft/article/details/90481925

相关文章

  • TypeScript基础入门之装饰器(三)

    转载 TypeScript基础入门之装饰器(三) 继续上篇文章[TypeScript基础入门之装饰器(二)] 访...

  • TS装饰器初体验,用装饰器管理koa接口

    typescript中的装饰器有很多种,比如类装饰器、方法装饰器、属性装饰器等等,先看看装饰器的定义吧,下面以类装...

  • 如何配置React项目直接使用TypeScript包(babel

    上期我们说到了TypeScript装饰器(decorators)和JavaScript装饰器编译出的代码不同,我们...

  • 迁移React项目至TypeScript(babel版)

    上期我们说到了TypeScript装饰器(decorators)和JavaScript装饰器编译出的代码不同,虽然...

  • 【TypeScript】装饰器

    在看VSCODE源码的时候,看到这样一部分代码: 疑惑之际,查看一下官方文档:https://www.tslang...

  • TypeScript装饰器

    装饰器是一种特殊类型的声明,它可以用在类声明、方法、属性或者参数上。顾名思义,它是用来给附着的主体进行装饰,添加额...

  • Typescript装饰器

    装饰器主要用于将一些常用操作进行抽象出一层对类,方法,属性,参数进行修饰的函数。常用例子:设置属性时,打日志等。 ...

  • 装饰器 TypeScript

    装饰器 : 装饰器是一种特殊类型的声名 。它能够被附加到类声明,方法,属性或参数上 , 可以修改类的行为。 通俗的...

  • TypeScript 装饰器

    TypeScript 与 ES6 的装饰器? ES6 的装饰器是一种函数,写成@ + 函数名。它可以放在类和类方法...

  • Typescript 装饰器

    我想绝大多数开发人员都见识过 java 里的 annotation,经典的 @ 图标, 如 @Override: ...

网友评论

      本文标题:typescript 五种装饰器

      本文链接:https://www.haomeiwen.com/subject/suaewctx.html