Typescript中的装饰器原理

类中不同声明上的装饰器将按以下规定的顺序应用:

1. 参数装饰器,然后依次是方法装饰器,访问符装饰器,或属性装饰器应用到每个实例成员。

2. 参数装饰器,然后依次是方法装饰器,访问符装饰器,或属性装饰器应用到每个静态成员。

3. 参数装饰器应用到构造函数。

4. 类装饰器应用到类。

1.1 类装饰

类的构造函数作为其唯一的参数。

1.1.1 举例:

1.	function decTest(constructor: Function) {
2.	    console.log(constructor("hello!"));
3.	}
4.	
5.	@decTest
6.	class Greeter {
7.	    greeting: string;
8.	    constructor(message: string) {
9.	        this.greeting = message;
10.	        console.log("in constructor:",message);
11.	    }
12.	    greet() {
13.	        return "Hello, " + this.greeting;
14.	    }
15.	}

第1行定义了一个装饰器函数decTest;

第5行在类Greeter类上安装了decTest装饰器。

编译后得到:

1.	var __decorate = (this && this._decorate) || function (decorators, target, key, desc) {
2.	    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3.	    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4.	    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;
5.	    return c > 3 && r && Object.defineProperty(target, key, r), r;
6.	};
7.	function decTest(constructor) {
8.	    console.log(constructor("hello!"));
9.	}
10.	var Greeter = (function () {
11.	    function Greeter(message) {
12.	        this.greeting = message;
13.	        console.log("in constructor:", message);
14.	    }
15.	    Greeter.prototype.greet = function () {
16.	        return "Hello, " + this.greeting;
17.	    };
18.	    Greeter = __decorate([
19.	        decTest
20.	    ], Greeter);
21.	    return Greeter;
22.	}());

说明:

第1行定义了一个__decorate函数,这个函数会处理类装饰器的功能。

第10行定义了一个函数,从第22行可以看出,这个函数是自调用的,在第18行,函数调用了__decorate函数。

1.1.2 简化

第1行中

(this && this._decorate)

是为了避免重复定义__decorate函数。注意javascript中&&运算符的奇怪的规则。如果__decorate函数未定义,这上述表达式的值为undefined,相当于false。于是得到:

var __decorate = function (decorators, target, key, desc) {…}

第18行看出,调用__decorate函数时只给了2个参数,一个是数组,其中包含多个类装饰器,这里只使用了一个类装饰器;另一个是Greeter类的构造函数Greeter()。这样,调用__decorate函数时decorators为一个装饰器数组;target为构造函数Greeter(),key和desc均未使用,值为undefined。这里的__decorate函数为各种装饰器的通用代码,在方法装饰器中key和desc均有使用。

第2行参数数量在类装饰器中为2,显然小于3。

第3行指出如果系统支持反射,则直接使用Reflect.decorate(decorators,target, key, desc)方法。否则自行定义实现装饰器机制的代码。

由此可以将编译后的js代码简化为:

1.	var __decorate = function (decorators, target, key, desc) {
2.	    r = target;
3.	    for (var i = decorators.length - 1; i >= 0; i--)
4.	        if (d = decorators[i]) r = d(r);
5.	    return r;
6.	};
7.	
8.	function decTest(constructor) {
9.	    console.log(constructor("hello!"));
10.	}
11.	var Greeter = (function () {
12.	    function Greeter(message) {
13.	        this.greeting = message;
14.	        console.log("in constructor:", message);
15.	    }
16.	    Greeter.prototype.greet = function () {
17.	        return "Hello, " + this.greeting;
18.	    };
19.	    Greeter = __decorate([
20.	        decTest
21.	    ], Greeter);
22.	    return Greeter;
23.	}());

第3行依次将构造函数Greeter(message)作为参数调用装饰器函数decTest。可以看出第3行实现了类装饰器的堆叠,堆叠规则是先处理后面的,后处理前面的。比如:

1.	@a
2.	@b
3.	class C {
4.	   …
5.	}

先处理装饰器b,再处理装饰器a。

第22行返回了构造函数Greeter(message)。最终第11行的变量Greeter就等于这个构造函数。注意:JavaScript中本质上对象都是通过函数创建的。

第19行实际上就是通过类装饰器函数修改了Greeter类的构造函数Greeter(message)的行为,从而修改了对象的特性,比如增加接口,注入类成员……等等。

var gt = new Greeter("My Hello");

输出:

in constructor: My Hello

1.1.3 Angular中的类装饰器

Angular中的组件这样写:

1.	import { Component } from '@angular/core';
2.	
3.	@Component({
4.	  selector: 'app-root',
5.	  templateUrl: './app.component.html',
6.	  styleUrls: ['./app.component.css']
7.	})
8.	export class AppComponent {
9.	  title = 'app';
10.	}

编译后为:

1.	"use strict";
2.	var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3.	    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4.	    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5.	    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;
6.	    return c > 3 && r && Object.defineProperty(target, key, r), r;
7.	};
8.	exports.__esModule = true;
9.	var core_1 = require("@angular/core");
10.	var AppComponent = (function () {
11.	    function AppComponent() {
12.	        this.title = 'app';
13.	    }
14.	    AppComponent = __decorate([
15.	        core_1.Component({
16.	            selector: 'app-root',
17.	            templateUrl: './app.component.html',
18.	            styleUrls: ['./app.component.css']
19.	        })
20.	    ], AppComponent);
21.	    return AppComponent;
22.	}());
23.	exports.AppComponent = AppComponent;

从第14行可以想见,Angular中@Component装饰器为组件类增加了一些类成员信息。

1.1 方法装饰器

1.1.1 举例

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

1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。

2、成员的名字。

3、成员的属性描述符。

    注意  如果代码输出目标版本小于ES5,属性描述符将会是undefined。

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

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

1.	function decoTest(value: boolean, str: string) {
2.	    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
3.	        descriptor.enumerable = value;
4.	        console.log("value:----",value);
5.	        console.log("str:----",str);
6.	        console.log("target----",target);
7.	        console.log("propertyKey----",propertyKey);
8.	        console.log("descriptor----",descriptor);
9.	
10.	    };
11.	}
12.	class Greeter {
13.	    greeting: string;
14.	    constructor(message: string) {
15.	        this.greeting = message;
16.	    }
17.	
18.	    @decoTest(false, "MyStr")
19.	    greet() {
20.	        return "Hello, " + this.greeting;
21.	    }
22.	}
23.	
24.	var gr = new Greeter("MyMessage");
25.	console.log(gr.greet());

第18行在greet函数上安装了装饰器,并带有2个参数;

第1行为装饰器函数,函数通过console.log函数显示参数值。

编译后为:

1.	var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2.	    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3.	    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4.	    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;
5.	    return c > 3 && r && Object.defineProperty(target, key, r), r;
6.	};
7.	function decoTest(value, str) {
8.	    return function (target, propertyKey, descriptor) {
9.	        descriptor.enumerable = value;
10.	        console.log("value:----", value);
11.	        console.log("str:----", str);
12.	        console.log("target----", target);
13.	        console.log("propertyKey----", propertyKey);
14.	        console.log("descriptor----", descriptor);
15.	    };
16.	}
17.	var Greeter = (function () {
18.	    function Greeter(message) {
19.	        this.greeting = message;
20.	    }
21.	    Greeter.prototype.greet = function () {
22.	        return "Hello, " + this.greeting;
23.	    };
24.	    __decorate([
25.	        decoTest(false, "MyStr")
26.	    ], Greeter.prototype, "greet", null);
27.	    return Greeter;
28.	}());
29.	var gr = new Greeter("MyMessage");
30.	console.log(gr.greet());

第1行定义了实现装饰器机制的函数__decorate,第24行调用该函数,包含4个参数:装饰器函数数组;Greeter函数的原型;安装了装饰器的函数greet;一个null值。

1.1.1 简化

第2行的参数数量大于3;desc的值为null;系统不支持ES6中的反射;

第24行未使用__decorate函数的返回值;

js代码可以简化为:

1.	var __decorate = function (decorators, target, key, desc) {
2.	    r = Object.getOwnPropertyDescriptor(target, key);
3.	    for (var i = decorators.length - 1; i >= 0; i--)
4.	        if (d = decorators[i])
5.	            d(target, key, r);
6.	};
7.	
8.	function decoTest(value, str) {
9.	    return function (target, propertyKey, descriptor) {
10.	        descriptor.enumerable = value;
11.	        console.log("value:----", value);
12.	        console.log("str:----", str);
13.	        console.log("target----", target);
14.	        console.log("propertyKey----", propertyKey);
15.	        console.log("descriptor----", descriptor);
16.	    };
17.	}
18.	var Greeter = (function () {
19.	    function Greeter(message) {
20.	        this.greeting = message;
21.	    }
22.	    Greeter.prototype.greet = function () {
23.	        return "Hello, " + this.greeting;
24.	    };
25.	    __decorate([
26.	        decoTest(false, "MyStr")
27.	    ], Greeter.prototype, "greet", null);
28.	    return Greeter;
29.	}());
30.	var gr = new Greeter("MyMessage");
31.	console.log(gr.greet());

程序输出:

value:---- false
str:---- MyStr
target---- Greeter { greet: [Function] }
propertyKey---- greet
descriptor---- { value: [Function],
  writable: true,
  enumerable: false,
  configurable: true }
Hello, MyMessage

第1行中,decorators为装饰器数组,支持多个装饰器;target为Greeter类的原型;key和propertyKey为装饰器的目标函数名字符串。

第3行支持装饰器堆叠,堆叠顺序从里到外。

注意第5行,引发的调用为decoTest(false,"MyStr")(target, key, r),decoTest(false,"MyStr")返回一个函数!

可以看出方法装饰器的功能:增加方法的功能,修改方法的属性,透过Greeter.prototype可以进而在类中增加类属性成员和函数成员。


  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值