13.1 装饰器是什么
- 它是一个表达式
- 该表达式被执行后,返回一个函数
- 函数的入参分别为 target、name 和 descriptor
执行该函数后,可能返回 descriptor 对象,用于配置 target 对象
13.2 装饰器的分类
类装饰器(Class decorators)
- 属性装饰器(Property decorators)
- 方法装饰器(Method decorators)
-
13.3 类装饰器
类装饰器声明:
declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
类装饰器顾名思义,就是用来装饰类的。它接收一个参数:
target: TFunction - 被装饰的类
看完第一眼后,是不是感觉都不好了。没事,我们马上来个例子:
function Greeter(target: Function): void {target.prototype.greet = function (): void {console.log("Hello Semlinker!");};}@Greeterclass Greeting {constructor() {// 内部实现}}let myGreeting = new Greeting();myGreeting.greet(); // console output: 'Hello Semlinker!';
上面的例子中,我们定义了 Greeter 类装饰器,同时我们使用了 @Greeter 语法糖,来使用装饰器。
友情提示:读者可以直接复制上面的代码,在 TypeScript Playground 中运行查看结果。
有的读者可能想问,例子中总是输出 Hello Semlinker! ,能自定义输出的问候语么 ?这个问题很好,答案是可以的。
具体实现如下:
function Greeter(greeting: string) {return function (target: Function) {target.prototype.greet = function (): void {console.log(greeting);};};}@Greeter("Hello TS!")class Greeting {constructor() {// 内部实现}}let myGreeting = new Greeting();myGreeting.greet(); // console output: 'Hello TS!';
13.4 属性装饰器
属性装饰器声明:
declare type PropertyDecorator = (target:Object,propertyKey: string | symbol ) => void;
属性装饰器顾名思义,用来装饰类的属性。它接收两个参数:
- target: Object - 被装饰的类
- propertyKey: string | symbol - 被装饰类的属性名
趁热打铁,马上来个例子热热身:
function logProperty(target: any, key: string) {delete target[key];const backingField = "_" + key;Object.defineProperty(target, backingField, {writable: true,enumerable: true,configurable: true});// property getterconst getter = function (this: any) {const currVal = this[backingField];console.log(`Get: ${key} => ${currVal}`);return currVal;};// property setterconst setter = function (this: any, newVal: any) {console.log(`Set: ${key} => ${newVal}`);this[backingField] = newVal;};// Create new property with getter and setterObject.defineProperty(target, key, {get: getter,set: setter,enumerable: true,configurable: true});}class Person {@logPropertypublic name: string;constructor(name : string) {this.name = name;}}const p1 = new Person("semlinker");p1.name = "kakuqo";
以上代码我们定义了一个 logProperty 函数,来跟踪用户对属性的操作,当代码成功运行后,在控制台会输出以下结果:
Set: name => semlinkerSet: name => kakuqo
13.5 方法装饰器
方法装饰器声明:
declare type MethodDecorator = <T>(target:Object, propertyKey: string | symbol,descriptor: TypePropertyDescript<T>) => TypedPropertyDescriptor<T> | void;
方法装饰器顾名思义,用来装饰类的方法。它接收三个参数:
- target: Object - 被装饰的类
- propertyKey: string | symbol - 方法名
- descriptor: TypePropertyDescript - 属性描述符
废话不多说,直接上例子:
function LogOutput(tarage: Function, key: string, descriptor: any) {let originalMethod = descriptor.value;let newMethod = function(...args: any[]): any {let result: any = originalMethod.apply(this, args);if(!this.loggedOutput) {this.loggedOutput = new Array<any>();}this.loggedOutput.push({method: key,parameters: args,output: result,timestamp: new Date()});return result;};descriptor.value = newMethod;}class Calculator {@LogOutputdouble (num: number): number {return num * 2;}}let calc = new Calculator();calc.double(11);// console ouput: [{method: "double", output: 22, ...}]console.log(calc.loggedOutput);
13.6 参数装饰器
参数装饰器声明:
declare type ParameterDecorator = (target: Object, propertyKey: string | symbol,parameterIndex: number ) => void
参数装饰器顾名思义,是用来装饰函数参数,它接收三个参数:
- target: Object - 被装饰的类
- propertyKey: string | symbol - 方法名
- parameterIndex: number - 方法中参数的索引值
介绍完 TypeScript 入门相关的基础知识,猜测很多刚入门的小伙伴已有 “从入门到放弃” 的想法,最后我们来简单介绍一下编译上下文。function Log(target: Function, key: string, parameterIndex: number) {let functionLogged = key || target.prototype.constructor.name;console.log(`The parameter in position ${parameterIndex} at ${functionLogged} hasbeen decorated`);}class Greeter {greeting: string;constructor(@Log phrase: string) {this.greeting = phrase;}}// console output: The parameter in position 0// at Greeter has been decorated
