装饰器简介

装饰器(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. codeforces-1111

    https://www.cnblogs.com/31415926535x/p/10397007.html codeforces 537 div2 A 题意就是给你两个字符串,然后如果s,t的对应位上的 ...

  2. 虚拟机Ping不通主机解决

    最近,装了一个虚拟机(Ubuntu-server-12.04),使用的桥接的方式.装完之后发现,主机可以ping通虚拟机,但是虚拟机可以ping通除主机之外的所有的IP(包括网关,DNS,还有其他的i ...

  3. Python3练习题系列(10)——项目骨架构建

    目标: 如何创建<项目“骨架”目录> 包含:项目文件布局.自动化测试代码,模组,以及安装脚本. 由于编写一个Python文件可以作为一个模块,一个带__init__.py的目录算一个包. ...

  4. 工具使用-----Jmeter教程 简单的压力测试

    摘抄于http://www.cnblogs.com/TankXiao/p/4059378.html 以下是英文版的,中文版的也差不多的 Jmeter是一个非常好用的压力测试工具.  Jmeter用来做 ...

  5. Oracle 拼接列数据的方法

    select wm_concat(fphone) phone from dq_phone_ndtbdxz wm_concat(列名):把多列值,合并成一列,用,隔开.

  6. ios多target开发

    链接: ios开发时,在Xcode中添加多个targets进行版本控制 如何在iOS项目中创建多个target 多个Target的使用 iOS开发中如何创建多个target

  7. register form

    <code class="language-html"><div class="width100 marT15 content_news_list&qu ...

  8. 继承之final关键字的使用

    final关键字 使用final关键字坐标识具有"最终的"含义, final可以修饰类.方法.属性.和变量. final修饰类表示该类不能被继承 final修饰方法,则表示该方法不 ...

  9. Golang 实现 set 集合,变相实现 切片去重、排序 功能

    Java 中的集合(set)去重很方便,PHP 中的数组值去重,就更加方便,一个函数搞定:array_unique(),Golang 中就比较苦逼了,官方没有提供对“切片去重”的功能,而项目中,又经常 ...

  10. python测试开发django-45.xadmin添加小组件报错解决

    前言 xadmin首页上有个添加小组件按钮,打开的时候会报错"render() got an unexpected keyword argument 'renderer'" 环境: ...