TypeScript学习笔记(九):装饰器(Decorators)
装饰器简介
装饰器(Decorators)为我们在类的声明及成员上通过元编程语法添加标注提供了一种方式。
需要注意的是:装饰器是一项实验性特性,在未来的版本中可能会发生改变。
若要启用实验性的装饰器特性,你必须在命令行或tsconfig.json里启用experimentalDecorators编译器选项:
{
"compilerOptions": {
"target": "ES5",
"experimentalDecorators": true
}
}
如何定义
装饰器使用@expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。
装饰器组合
多个装饰器可以同时应用到一个声明上,就像下面的示例:
书写在同一行上:
@f @g x
书写在多行上:
@f
@g
x
在TypeScript里,当多个装饰器应用在一个声明上时会进行如下步骤的操作:
- 由上至下依次对装饰器表达式求值。
- 求值的结果会被当作函数,由下至上依次调用。
类装饰器
类装饰器在类声明之前被声明(紧靠着类声明)。 类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。 类装饰器不能用在声明文件中( .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个参数:
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- 成员的名字。
- 成员的属性描述符。
注意:如果代码输出目标版本小于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个参数:
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- 成员的名字。
- 成员的属性描述符。
注意:如果代码输出目标版本小于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个参数:
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- 成员的名字。
我们来看一个例子:
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个参数:
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- 成员的名字。
- 参数在函数参数列表中的索引。
我们来看一个例子:
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)的更多相关文章
- Python学习笔记012——装饰器
1 装饰器 1.1装饰器定义 在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator). 1.2 装饰器分类 装饰器:函数装饰器,类装饰器,函数的装饰器,类的装饰器 装饰器:函数装饰函 ...
- Python学习笔记:装饰器
Python 装饰器的基本概念和应用 代码编写要遵循开放封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即: 封闭:已 ...
- python学习笔记之装饰器、递归、算法(第四天)
参考老师的博客: 金角:http://www.cnblogs.com/alex3714/articles/5161349.html 银角:http://www.cnblogs.com/wupeiqi/ ...
- python学习笔记:装饰器2
python的装饰器本质是函数,为了不改变装饰目标函数内部代码而增加额外功能而存在 一.一般装饰函数实例: import datetime def func_name(func):#定义一个装饰函数, ...
- Python学习笔记之装饰器原理
def decorator(fn): def wrapper(): print("询价") fn() print("购买成功!") return wrapper ...
- python学习笔记(五):装饰器、生成器、内置函数、json
一.装饰器 装饰器,这个器就是函数的意思,连起来,就是装饰函数,装饰器本身也是一个函数,它的作用是用来给其他函数添加新功能,比如说,我以前写了很多代码,系统已经上线了,但是性能比较不好,现在想把程序里 ...
- Python学习日记(九) 装饰器函数
1.import time a.time.time() 获取到当前的时间,返回值为浮点型 import time print(time.time()) #1565422783.6497557 b.ti ...
- python学习笔记之装饰器、生成器、内置函数、json(五)
一.装饰器 装饰器,这个器就是函数的意思,连起来,就是装饰函数,装饰器本身也是一个函数,它的作用是用来给其他函数添加新功能比如说,我以前写了很多代码,系统已经上线了,但是性能比较不好,现在想把程序里面 ...
- SpringMVC学习笔记九:拦截器及拦截器的简单实用
SpringMVC中的interceptor拦截器是非常重要的,它的主要作用就是拦截指定的用户请求,并进行相应的预处理和后处理. 拦截时间点在"处理器映射器根据用户提交的请求映射出所要执行的 ...
- python笔记 - day4-之装饰器
python笔记 - day4-之装饰器 需求: 给f1~f100增加个log: def outer(): #定义增加的log print("log") ...
随机推荐
- 编辑你的数学公式——markdown中latex的使用
前言 最近开始使用起markdown来记学习笔记,因为经常有公式要写,就需要用到latex,到网上查来查去又不太方便,而且也很少能查到写的比较全的,就准备写下这篇文章. 插入数学公式 在markdow ...
- Ubuntu crontab 定时 python 详细
Ubuntu系统,定时执行python脚本. 目的:每分钟执行一次timer_test.py timer_test.py 路径 /home/li/d/pythonwork/test/timer_ ...
- IT常用英文术语解释发音
associations 联系; 协会( association的名词复数 ); 联合; (思想.感觉.记忆等的) 联想; 按色赛诶神 == desktop ˈdesktɒp 的思克头铺 英[ˈde ...
- 51Nod.1244.莫比乌斯函数之和(杜教筛)
题目链接 map: //杜教筛 #include<map> #include<cstdio> typedef long long LL; const int N=5e6; in ...
- 潭州课堂25班:Ph201805201 WEB 之 CSS 第二课 (课堂笔记)
CSS 的引入方法: 第一种 : <!--直接在标签仙设置--><p style="color: yellow">CSS的第一种引入方法</p> ...
- Android getWidth和getMeasuredWidth
1. 在一个类初始化时,即在构造函数当中我们是得不到View的实际大小的.感兴趣的朋友可以试一下,getWidth()和getMeasuredWidth()得到的结果都是0.但是我们可以从onDraw ...
- 使用 SHOW STATUS 查看mysql 服务器状态信息
在LAMP架构的网站开发过程中,有些时候我们需要了解MySQL的服务器状态信息,譬如当前MySQL启动后的运行时间,当前MySQL的客户端会话连接数,当前MySQL服务器执行的慢查询数,当前MySQL ...
- 【3dsmax2016】安装图文教程、破解注册以及切换语言方法
第一步:安装3dsmax2014 64位中文版 第二步:解压3dsmax2014 64位中文版安装包 第三步:选择安装产品 第四步:接受相关协议 第五步:产品序列号和密钥 第六步:产品子组件选择 第七 ...
- 将python文件加入到python的环境变量中
在python中,把一个python文件加入到python环境变量中,以方便其他python文件调用.有两种方式,下面对其进行简单的介绍 方式一: 导入系统自带的sys模块,然后将文件的路径进行追加或 ...
- LeetCode全文解锁 √
分享一波大牛整理leetcode,方便整理思路 可以点击下载