1. .装饰器的概念
· 装饰器是一种特殊类型的声明,它能够被附加到类的声明,方法,访问符,属性或参数上;
· 装饰器使用 @expression 这种形式,expression求值后必须为一个函数;
· 它会在运行时被调用,被装饰的声明信息做为参数传入。
· 装饰器执行时机是在代码编译时发生的(不是 TypeScript 编译,而是在 JavaScript 执行编译阶段)
启用装饰器特性,必须在命令行或 tsconfig.json 里启用 experimentalDecorators 编译器选项:
2. 装饰器语法
启用装饰器特性,必须在命令行或 tsconfig.json 里启用 experimentalDecorators 编译器选项:
// 在命令行中使用 tsc--target ES5--experimentalDecorators // tsconfig.json 中开启: { "compilerOptions": { "target": "ES5", "experimentalDecorators": true } }
装饰器定义与应用举例:
// 装饰器定义 function sealed(target) { // do something with "target" ... } // 或使用装饰器工厂函数 function color(value: string) { // 这是一个装饰器工厂 return function (target) { // 这是装饰器 // do something with "target" and "value"... } } // 应用到一个声明上,例如: @sealed @color let x: string; // 或 @sealed @color let x: string
在 TypeScript 里,当多个装饰器应用在一个声明上时会进行如下步骤的操作:
· 由上至下依次对装饰器表达式求值。
· 求值的结果会被当作函数,由下至上依次调用。
多个装饰器应用在一个声明,举例:
// 多个装饰器应用在一个声明 function f() { console.log("f(): evaluated"); return function (target, propertyKey: string) { console.log("f(): called"); } } function g() { console.log("g(): evaluated"); return function (target, propertyKey: string) { console.log("g(): called"); } } class C { @f() @g() method() { } } // f(): evaluated // g(): evaluated // g(): called // f(): called
3. 类装饰器
类中不同声明上的装饰器将按以下规定的顺序应用:
· 有多个参数装饰器时:从最后一个参数依次向前执行。
· 方法和方法参数中,参数装饰器先执行。
· 方法和属性装饰器,谁在前面谁先执行。因为参数属于方法一部分,所以参数会挨着方法执行。
· 类装饰器总是最后执行。
类装饰器在类声明之前被声明(紧靠着类声明)。
类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。
// 类装饰符实例 function Path(path: string) { return function (target: Function) { !target.prototype.$Meta && (target.prototype.$Meta = {}) target.prototype.$Meta.baseUrl = path; }; } @Path(‘/hello‘) class HelloService { [x: string]: any; constructor() { } } console.log(HelloService.prototype.$Meta); // => { baseUrl: ‘/hello‘ } let hello = new HelloService(); console.log(hello.$Meta) // => { baseUrl: ‘/hello‘ }
4. 方法装饰器
方法装饰器声明在一个方法的声明之前,可以用来监视,修改或者替换方法定义。
方法装饰器表达式会在运行时当作函数被调用,传入下列3个参数:
· 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象;
· 成员的名字;
· 成员的属性描述符;
注意:如果代码输出目标版本小于ES5,属性描述符将会是 undefined,返回值会被忽略。 如果方法装饰器返回一个值,它会被用作方法的属性描述符。
方法装饰器,举例:
// 方法装饰符实例 function GET(alias: string) { return function (target, methodName: string, descriptor: PropertyDescriptor) { target._alias = alias; } } class HelloService { _alias: string; constructor() { } @GET("getMyName") getUser() { } } let hello = new HelloService(); console.log(hello._alias); // => gtMyName
5. 访问装饰器
访问器装饰器声明在一个访问器的声明之前,可以用来监视,修改或替换一个访问器的定义。
访问装饰器表达式会在运行时当作函数被调用,传入下列3个参数:
· 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象;
· 成员的名字;
· 成员的属性描述符;
注意:如果代码输出目标版本小于ES5,属性描述符将会是 undefined,返回值会被忽略。
如果访问装饰器返回一个值,它会被用作方法的属性描述符。
TypeScript 不允许同时装饰一个成员的get和set访问器;
一个成员的所有装饰的必须应用在文档顺序的第一个访问器上;因为在装饰器应用于一个属性描述 符时,它联 合了get和set访问器,而不是分开声明的。
访问装饰器,举例:
// 访问装饰器实例 function access(value: string) { return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { !target.$Meta && (target.$Meta = {}) target.$Meta[propertyKey] = `It‘s a ${value} method` }; } class Point { private _x: number; private _y: number; constructor(x: number, y: number) { this._x = x; this._y = y; } @access(‘get‘) get x() { return this._x; } @access(‘set‘) set y(y: number) { this._y = y; } } let point = new Point(1, 2); console.log(point[‘$Meta‘]); // => { x: "It‘s a get method", y: "It‘s a set method" }
6. .属性装饰器
属性装饰器声明在一个属性声明之前。
属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数:
· 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
· 成员的名字。
注意: 属性描述符不会做为参数传入属性装饰器,因为目前无法在定义一个原型对象的成员时描述一个实例属性,且无法监视或修改一个属性的初始化方法,因此,属性描述符只能用来监视类中是否声明了某个名字的属性。
属性装饰器,举例:
// 属性装饰符实例 function DefaultValue(value: string) { return function (target: any, propertyName: string) { target[propertyName] = value; } } class Hello { @DefaultValue("world") greeting: string; } console.log(new Hello().greeting); // => world
7. 参数装饰器
参数装饰器声明在一个参数声明之前, 参数装饰器应用于类构造函数或方法声明。
参数装饰器表达式会在运行时当作函数被调用,传入下列3个参数:
· 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象;
· 成员的名字;
· 参数在函数参数列表中的索引;
注意:
参数装饰器只能用来监视一个方法的参数是否被传入。
参数装饰器的返回值会被忽略。
参数装饰器,举例:
// 参数装饰器实例 function PathParam(paramName: string) { return function (target, methodName: string, paramIndex: number) { !target.$Meta && (target.$Meta = {}); target.$Meta[paramIndex] = paramName; } } class HelloService { constructor() { } getUser( @PathParam("the user‘s id") userId: string ) { } } console.log((<any>HelloService).prototype.$Meta); // => { ‘0‘: "the user‘s id" }
原文:https://www.cnblogs.com/JosephWong/p/13684327.html