介绍
随着TypeScript和ES6里引入了类,在一些场景下我们需要额外的特性来支持标注或修改类及其成员。 装饰器(Decorators)为我们在类的声明及成员上通过元编程语法添加标注提供了一种方式。 Javascript里的装饰器目前处在 建议征集的第二阶段,但在TypeScript里已做为一项实验性特性予以支持。
使用
命令行:
tsc --target ES5 --experimentalDecorators
tsconfig.json:
{
"compilerOptions": {
"target": "ES5",
"experimentalDecorators": true
}
}
装饰器定义
装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上。 装饰器使用 @expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。
基础使用
target指向的是HelloWordClass
function helloWord(target: any) {
console.log('hello Word!');
}
@helloWord
class HelloWordClass {
}
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 helloWord(target) {
console.log('hello Word!');
}
var HelloWordClass = /** @class */ (function () {
function HelloWordClass() {
}
HelloWordClass = __decorate([
helloWord
], HelloWordClass);
return HelloWordClass;
}());
这是编译过后的代码,是声明一个__decorate,把HelloWordClass传给前面的fn数组
类装饰器
应用于类构造函数,其参数是类的构造函数。
function addAge(args: number) {
return function (target: Function) {
target.prototype.age = args;
};
}
@addAge(18)
class Hello {
name: string;
age: number;
constructor() {
console.log('hello');
this.name = 'yugo';
}
}
console.log(Hello.prototype.age);//18
let hello = new Hello();
console.log(hello.age);//18
方法装饰器
它会被应用到方法的 属性描述符上,可以用来监视,修改或者替换方法定义。
方法装饰会在运行时传入下列3个参数:
1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
2、成员的名字。
3、成员的属性描述符{value: any, writable: boolean, enumerable: boolean, configurable: boolean}。
function addAge(constructor: Function) {
constructor.prototype.age = 18;
}
function method(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log(target);
console.log("prop " + propertyKey);
console.log("desc " + JSON.stringify(descriptor) + "\n\n");
};
@addAge
class Hello{
name: string;
age: number;
constructor() {
console.log('hello');
this.name = 'yugo';
}
@method
hello(){
return 'instance method';
}
@method
static shello(){
return 'static method';
}
}
访问器装饰器
访问器装饰器应用于访问器的属性描述符,可用于观察,修改或替换访问者的定义。 访问器装饰器不能在声明文件中使用,也不能在任何其他环境上下文中使用(例如在声明类中)。
class Point {
private _x: number;
private _y: number;
constructor(x: number, y: number) {
this._x = x;
this._y = y;
}
@configurable(false)
get x() { return this._x; }
@configurable(false)
get y() { return this._y; }
}
方法参数装饰器
参数装饰器表达式会在运行时当作函数被调用
const parseConf = [];
class Modal {
@parseFunc
public addOne(@parse('number') num) {
console.log('num:', num);
return num + 1;
}
}
// 在函数调用前执行格式化操作
function parseFunc(target, name, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
for (let index = 0; index < parseConf.length; index++) {
const type = parseConf[index];
console.log(type);
switch (type) {
case 'number':
args[index] = Number(args[index]);
break;
case 'string':
args[index] = String(args[index]);
break;
case 'boolean':
args[index] = String(args[index]) === 'true';
break;
}
return originalMethod.apply(this, args);
}
};
return descriptor;
}
// 向全局对象中添加对应的格式化信息
function parse(type) {
return function (target, name, index) {
parseConf[index] = type;
console.log('parseConf[index]:', type);
};
}
let modal = new Modal();
console.log(modal.addOne('10')); // 11
属性装饰器
属性装饰器表达式会在运行时当作函数被调用,
function log(target: any, propertyKey: string) {
let value = target[propertyKey];
// 用来替换的getter
const getter = function () {
console.log(`Getter for ${propertyKey} returned ${value}`);
return value;
}
// 用来替换的setter
const setter = function (newVal) {
console.log(`Set ${propertyKey} to ${newVal}`);
value = newVal;
};
// 替换属性,先删除原先的属性,再重新定义属性
if (delete this[propertyKey]) {
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
}
}
class Calculator {
@log
public num: number;
square() {
return this.num * this.num;
}
}
let cal = new Calculator();
cal.num = 2;
console.log(cal.square());
// Set num to 2
// Getter for num returned 2
// Getter for num returned 2
// 4