类中不同声明上的装饰器将按以下规定的顺序应用:
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可以进而在类中增加类属性成员和函数成员。