装饰器简介

装饰器(Decorators)为我们在类的声明及成员上通过元编程语法添加标注提供了一种方式。

需要注意的是:装饰器是一项实验性特性,在未来的版本中可能会发生改变。

若要启用实验性的装饰器特性,你必须在命令行或tsconfig.json里启用experimentalDecorators编译器选项:

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

如何定义

装饰器使用@expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。

装饰器组合

多个装饰器可以同时应用到一个声明上,就像下面的示例:

书写在同一行上:

@f @g x

书写在多行上:

@f
@g
x

在TypeScript里,当多个装饰器应用在一个声明上时会进行如下步骤的操作:

  1. 由上至下依次对装饰器表达式求值。
  2. 求值的结果会被当作函数,由下至上依次调用。

类装饰器

类装饰器在类声明之前被声明(紧靠着类声明)。 类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。 类装饰器不能用在声明文件中( .d.ts),也不能用在任何外部上下文中(比如declare的类)。

类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。

我们来看一个例子:

 function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
} @sealed
class MyClass {
a: number = 0;
b: string = "hello";
} var obj = new MyClass();
// obj.c = true; // 编译报错

通过类装饰器我们可以对类的原型对象做一定的修改。

编译后的源码:

 "use strict";
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 sealed(constructor) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
var MyClass = (function () {
function MyClass() {
this.a = 0;
this.b = "hello";
}
MyClass = __decorate([
sealed
], MyClass);
return MyClass;
}());
var obj = new MyClass();
// obj.c = true; // 编译报错

这里我们只关注传递到装饰函数的constructor参数:就是我们定义的MyClass对象。

方法装饰器

方法装饰器声明在一个方法的声明之前(紧靠着方法声明)。 它会被应用到方法的 属性描述符上,可以用来监视,修改或者替换方法定义。 方法装饰器不能用在声明文件( .d.ts),重载或者任何外部上下文(比如declare的类)中。

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

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

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

我们来看下面的例子:

 function methodDecorator(param1: boolean, param2?: string) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log(param1 + ", " + param2 + ", " + target + ", " + propertyKey + ", " + JSON.stringify(descriptor));
};
} class MyClass {
@methodDecorator(true, "this is static")
public static sFunc(): void {
console.log("call static method");
} @methodDecorator(false)
public func(): void {
console.log("call method");
}
} MyClass.sFunc();
MyClass.sFunc(); var obj = new MyClass();
obj.func();
obj.func();

输出如下:

 false, undefined, [object Object], func, {"writable":true,"enumerable":true,"configurable":true}
true, this is static, function MyClass() {
}, sFunc, {"writable":true,"enumerable":true,"configurable":true}
call static method
call static method
call method
call method

我们可以发现,方法装饰器返回的函数会在解释类的对应方法时被调用一次,并可以得到装饰器的参数和被装饰的方法的相关信息。

装饰器方法的调用只会在加载代码时执行一次,调用被装饰的方法不会触发装饰器方法。

编译后的源码:

 "use strict";
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 methodDecorator(param1, param2) {
return function (target, propertyKey, descriptor) {
console.log(param1 + ", " + param2 + ", " + target + ", " + propertyKey + ", " + JSON.stringify(descriptor));
};
}
var MyClass = (function () {
function MyClass() {
}
MyClass.sFunc = function () {
console.log("call static method");
};
MyClass.prototype.func = function () {
console.log("call method");
};
__decorate([
methodDecorator(false)
], MyClass.prototype, "func", null);
__decorate([
methodDecorator(true, "this is static")
], MyClass, "sFunc", null);
return MyClass;
}());
MyClass.sFunc();
MyClass.sFunc();
var obj = new MyClass();
obj.func();
obj.func();

这里我们只关注传递到装饰函数的target参数:

装饰静态方法时是定义的MyClass对象;

装饰动态方法时是定义的MyClass对象的原型对象,在24行这里“], MyClass.prototype, "func", null);”,我们知道,MyClass的原型对象在没有继承时是一个Object对象,该对象是所有MyClass的实例共用的,在有继承时,MyClass的原型对象会是一个新创建的对象,并且会对父类的方法进行浅复制的对象,如下:

 var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
// ...
var MyClass = (function () {
function MyClass() {
}
// ...
return MyClass;
}());
var MyClass2 = (function (_super) {
__extends(MyClass2, _super);
function MyClass2() {
return _super !== null && _super.apply(this, arguments) || this;
}
// ...
return MyClass2;
}(MyClass));

此时如果操作target对象,比如为该对象添加一个属性,那么这个属性MyClass类的所有实例都可以访问到。

访问器装饰器

访问器装饰器声明在一个访问器的声明之前(紧靠着访问器声明)。 访问器装饰器应用于访问器的 属性描述符并且可以用来监视,修改或替换一个访问器的定义。 访问器装饰器不能用在声明文件中(.d.ts),或者任何外部上下文(比如 declare的类)里。

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

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

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

同时TypeScript不允许同时装饰一个成员的get和set访问器。取而代之的是,一个成员的所有装饰的必须应用在文档顺序的第一个访问器上。这是因为,在装饰器应用于一个属性描述符时,它联合了get和set访问器,而不是分开声明的。

我们来看一个例子:

 function methodDecorator(param1: boolean, param2?: string) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log(param1 + ", " + param2 + ", " + target + ", " + propertyKey + ", " + JSON.stringify(descriptor));
};
} class MyClass {
private static _myName: string; @methodDecorator(true, "this is static")
public static set myName(value: string) {
this._myName = value;
} public static get myName(): string {
return this._myName;
} private _age: number; @methodDecorator(false)
public set age(value: number) {
this._age = value;
} public get age(): number {
return this._age;
}
} MyClass.myName = "hello";
console.log(MyClass.myName); var obj = new MyClass();
obj.age = 28;
console.log(obj.age);

输出如下:

 false, undefined, [object Object], age, {"enumerable":true,"configurable":true}
true, this is static, function MyClass() {
}, myName, {"enumerable":true,"configurable":true}
hello
28

我们可以发现,访问器装饰器返回的函数会在解释类的对应访问器时被调用一次,并可以得到装饰器的参数和被装饰的访问器的相关信息。

装饰器方法的调用只会在加载代码时执行一次,调用被装饰的访问器不会触发装饰器方法。

编译后的源码:

 "use strict";
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 methodDecorator(param1, param2) {
return function (target, propertyKey, descriptor) {
console.log(param1 + ", " + param2 + ", " + target + ", " + propertyKey + ", " + JSON.stringify(descriptor));
};
}
var MyClass = (function () {
function MyClass() {
}
Object.defineProperty(MyClass, "myName", {
get: function () {
return this._myName;
},
set: function (value) {
this._myName = value;
},
enumerable: true,
configurable: true
});
Object.defineProperty(MyClass.prototype, "age", {
get: function () {
return this._age;
},
set: function (value) {
this._age = value;
},
enumerable: true,
configurable: true
});
__decorate([
methodDecorator(false)
], MyClass.prototype, "age", null);
__decorate([
methodDecorator(true, "this is static")
], MyClass, "myName", null);
return MyClass;
}());
MyClass.myName = "hello";
console.log(MyClass.myName);
var obj = new MyClass();
obj.age = 28;
console.log(obj.age);

请参考方法装饰器。

属性装饰器

属性装饰器声明在一个属性声明之前(紧靠着属性声明)。 属性装饰器不能用在声明文件中(.d.ts),或者任何外部上下文(比如 declare的类)里。

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

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

我们来看一个例子:

 function propDecorator(param1: boolean, param2?: string) {
return function (target: any, propertyKey: string) {
console.log(param1 + ", " + param2 + ", " + target + ", " + propertyKey);
};
} class MyClass {
@propDecorator(false, "Hi")
public static A: number = 0; @propDecorator(true)
public a: string = "hello";
} console.log(MyClass.A);
var obj = new MyClass();
console.log(obj.a);

输出如下:

 true, undefined, [object Object], a
false, Hi, function MyClass() {
this.a = "hello";
}, A
0
hello

我们可以发现,属性装饰器返回的函数会在解释类的对应属性时被调用一次,并可以得到装饰器的参数和被装饰的属性的相关信息。

装饰器方法的调用只会在加载代码时执行一次,调用被装饰的属性不会触发装饰器方法。

编译后的源码:

 "use strict";
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 propDecorator(param1, param2) {
return function (target, propertyKey) {
console.log(param1 + ", " + param2 + ", " + target + ", " + propertyKey);
};
}
var MyClass = (function () {
function MyClass() {
this.a = "hello";
}
MyClass.A = 0;
__decorate([
propDecorator(true)
], MyClass.prototype, "a", void 0);
__decorate([
propDecorator(false, "Hi")
], MyClass, "A", void 0);
return MyClass;
}());
console.log(MyClass.A);
var obj = new MyClass();
console.log(obj.a);

请参考方法装饰器。

参数装饰器

参数装饰器声明在一个参数声明之前(紧靠着参数声明)。 参数装饰器应用于类构造函数或方法声明。 参数装饰器不能用在声明文件(.d.ts),重载或其它外部上下文(比如 declare的类)里。

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

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  2. 成员的名字。
  3. 参数在函数参数列表中的索引。

我们来看一个例子:

 function paramDecorator(target: Object, propertyKey: string | symbol, parameterIndex: number) {
console.log(target + ", " + <any> propertyKey + ", " + parameterIndex);
} class MyClass {
public func(@paramDecorator a: number, @paramDecorator b: string = "hello", @paramDecorator c?: boolean): void {
console.log("call method");
}
} var obj = new MyClass();
obj.func(1);
obj.func(2);

输出如下:

 [object Object], func, 2
[object Object], func, 1
[object Object], func, 0
call method
call method

我们可以发现,参数装饰器返回的函数会在解释方法的参数时被调用一次,并可以得到参数的相关信息。我们有3个参数就调用了3次。

装饰器方法的调用只会在加载代码时执行一次,调用被装饰的参数的方法不会触发装饰器方法。

编译后的源码:

 "use strict";
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;
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
function paramDecorator(target, propertyKey, parameterIndex) {
console.log(target + ", " + propertyKey + ", " + parameterIndex);
}
var MyClass = (function () {
function MyClass() {
}
MyClass.prototype.func = function (a, b, c) {
if (b === void 0) { b = "hello"; }
console.log("call method");
};
__decorate([
__param(0, paramDecorator), __param(1, paramDecorator), __param(2, paramDecorator)
], MyClass.prototype, "func", null);
return MyClass;
}());
var obj = new MyClass();
obj.func(1);
obj.func(2);

请参考方法装饰器。

TypeScript学习笔记(九):装饰器(Decorators)的更多相关文章

  1. Python学习笔记012——装饰器

    1 装饰器 1.1装饰器定义 在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator). 1.2 装饰器分类 装饰器:函数装饰器,类装饰器,函数的装饰器,类的装饰器 装饰器:函数装饰函 ...

  2. Python学习笔记:装饰器

    Python 装饰器的基本概念和应用 代码编写要遵循开放封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即: 封闭:已 ...

  3. python学习笔记之装饰器、递归、算法(第四天)

    参考老师的博客: 金角:http://www.cnblogs.com/alex3714/articles/5161349.html 银角:http://www.cnblogs.com/wupeiqi/ ...

  4. python学习笔记:装饰器2

    python的装饰器本质是函数,为了不改变装饰目标函数内部代码而增加额外功能而存在 一.一般装饰函数实例: import datetime def func_name(func):#定义一个装饰函数, ...

  5. Python学习笔记之装饰器原理

    def decorator(fn): def wrapper(): print("询价") fn() print("购买成功!") return wrapper ...

  6. python学习笔记(五):装饰器、生成器、内置函数、json

    一.装饰器 装饰器,这个器就是函数的意思,连起来,就是装饰函数,装饰器本身也是一个函数,它的作用是用来给其他函数添加新功能,比如说,我以前写了很多代码,系统已经上线了,但是性能比较不好,现在想把程序里 ...

  7. Python学习日记(九) 装饰器函数

    1.import time a.time.time() 获取到当前的时间,返回值为浮点型 import time print(time.time()) #1565422783.6497557 b.ti ...

  8. python学习笔记之装饰器、生成器、内置函数、json(五)

    一.装饰器 装饰器,这个器就是函数的意思,连起来,就是装饰函数,装饰器本身也是一个函数,它的作用是用来给其他函数添加新功能比如说,我以前写了很多代码,系统已经上线了,但是性能比较不好,现在想把程序里面 ...

  9. SpringMVC学习笔记九:拦截器及拦截器的简单实用

    SpringMVC中的interceptor拦截器是非常重要的,它的主要作用就是拦截指定的用户请求,并进行相应的预处理和后处理. 拦截时间点在"处理器映射器根据用户提交的请求映射出所要执行的 ...

  10. python笔记 - day4-之装饰器

                 python笔记 - day4-之装饰器 需求: 给f1~f100增加个log: def outer(): #定义增加的log print("log") ...

随机推荐

  1. 【Java】 剑指offer(24) 反转链表

    本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头 ...

  2. 057 Hive项目案例过程

    1.说明 这里只粘贴一张,图,主要针对的hive的项目的实践过程. 2.图 3.需求 统计PV 统计注册人数 => 这个是一个公司会关注的,每天的注册率. 统计ip 统计跳出率 => ip ...

  3. 《Gradle权威指南》--Gradle插件

    No1: 应用插件 apply plugin:'java' apply plugin:org.gradle.api.plugins.JavaPlugin apply plugin:JavaPlugin ...

  4. 洛谷 P1057 传球游戏 【dp】(经典)

    题目链接:https://www.luogu.org/problemnew/show/P1057 题目描述 上体育课的时候,小蛮的老师经常带着同学们一起做游戏.这次,老师带着同学们一起做传球游戏. 游 ...

  5. 算法进阶面试题04——平衡二叉搜索树、AVL/红黑/SB树、删除和调整平衡的方法、输出大楼轮廓、累加和等于num的最长数组、滴滴Xor

    接着第三课的内容和讲了第四课的部分内容 1.介绍二叉搜索树 在二叉树上,何为一个节点的后继节点? 何为搜索二叉树? 如何实现搜索二叉树的查找?插入?删除? 二叉树的概念上衍生出的. 任何一个节点,左比 ...

  6. CPU HQ 什么意思

    CPU HQ 什么意思 High performance graphics, quad core 高性能图形,四核 参见:https://www.intel.cn/content/www/cn/zh/ ...

  7. C语言--isspace()函数实现

      c语言中有很多处理string类型的函数,今天简单的使用了一下isspace()函数,自我实现调用了一下. Isspace()函数   包含在ctype.h头文件中 函数原型: int isspa ...

  8. BZOJ.2639.矩形计算(二维莫队)

    题目链接 二维莫队,按x,y坐标一起分块.(x,y)的所属的块为 x/sq(n)*sq(m) + y/sq(m) 排序时按照(左下点所在块,右上点的标号)排序 排序后 先得出一个询问的答案,然后利用上 ...

  9. 报错:[__NSArrayI objectAtIndex:]: index 5 beyond bounds [0 .. 4]'

    报错内容:如下 分析: 遇到这种情况,说明超出了数组的范围 如要插入某组数据,但是这组数据只有10条:但是这里设置为20条.当第11个cell填充数据时就会报错, [__NSArrayI object ...

  10. Unity3D引擎中特殊的文件夹

    Editor Editor文件夹可以在根目录下,可以在子目录里,只要名是Editor就可以./xxx/xxx/Editor  和 /Editor 是一样的,多少个叫Editor的文件夹都可以.Edit ...