测试环境
下面的实践均采用ts编写,使用ts-node直接运行,需在全局安装typescipt
与ts-node
全局安装依赖
npm i typescript ts-node -g
运行ts文件
ts-node xx.ts
类装饰器
装饰器对象是一个类
function classDecorator(target){ return target } @classDecorator class C{ hello(){ console.log('hello world') } }
- target表示装饰的目标类
示例:在目标类上拓展 sayHello 方法
function helloWorld(target){ // target === 目标类 target.prototype.sayHello = function(){ console.log('hello world'); } return target } @helloWorld class Router { sayHello() { throw new Error("Method not implemented."); } } const r = new Router() r.sayHello() // hello world
函数装饰器
装饰对象的值是一个函数
function funDecorator(target, name, descriptor){ return descriptor.value } class C{ @funDecorator hello(){ console.log('hello world') } }
- target: 目标类的原型
- name: 属性名
- descriptor:属性描述符号
- value:属性值
- writable:是否可以被重写
- enumerable:是否可枚举
- configurable:是否可配置
示例:提示某个方法已经失效
function expired(target, name, descriptor): any { console.log('fun:',name, 'is expired'); return descriptor.value } @helloWorld class Router { @expired hello() { // ...code } @expired hello2(){ // ...code } } // fun: hello is expired // fun: hello2 is expired
get/set装饰器
装饰存/取器的装饰器
- target: 静态方法指向类的构造函数,实例方法指向类的原型
- name: 属性名
- descriptor:属性描述符号
- value:属性值
- writable:是否可以被重写
- enumerable:是否可枚举
- configurable:是否可配置
function getDecorator(target,name,descriptor){ } function getDecorator(target,name,descriptor){ } class C{ private _x: number private _y: number constructor(x, y) { this._x = x this._y = y } @getDecorator get x() { return this._x } @getDecorator get y() { return this._y } @setDecorator set x(v) { this._x = v } @getDecorator set y(v) { this._y = v } }
emmmm,暂没想到用武之地
属性装饰器
简而言之就是装饰对象是一个普通的属性,参数和上述函数装饰器一致
函数也可以看做是类的一个属性,只不过其属性值是function
function propertyDecorator(target,name){ } class C{ @propertyDecorator private property:string|undefined }
示例:设置默认值
function defaultValue(v: any) { return function (target, name) { target[name] = v } } class Router { @defaultValue('666') public name: string | undefined } const r = new Router() console.log(r.name);
其中装饰器传参实现,可以看做是一个闭包调用
在设置装饰器的时候传递参数,然后返回一个函数去真正装饰目标对象
这个被装饰的对象可以使用传递的参数
函数参数装饰器
顾名思义,装饰对象是函数中的一个参数
function paramDecorator(target,name,idx){ } class C{ fun1(@paramDecorator a){ } static func2(@paramDecorator a){ } }
- target: 静态方法指向类的构造函数,实例方法指向类的原型
- name:属性名
- idx:参数在函数中所处位置
示例:获取参数在函数中的相对位置
function printParamPos(target, name, idx) { console.log('paramCount:', target[name].length, 'paramPos:', idx); } class Router { hello3(@printParamPos name: string) { console.log('hello3', name); } static hello4(@printParamPos name: string) { console.log('hello4', name); } } const r = new Router() Router.hello4('123') r.hello3('456') // paramCount: 1 paramPos: 0 // paramCount: 1 paramPos: 0 // hello4 123 // hello3 456
实例
这里参考了一下core-decorators中的源码
弃用API提示
/** * 弃用指定API提示 * @param msg * @returns */ export function deprecate(msg = 'This function will be removed in future versions.') { return function (target, key, descriptor) { const methodSignature = `${target.constructor.name}#${key}`; return { ...descriptor, value: function () { console.warn(`DEPRECATION ${methodSignature}: ${msg}`); return descriptor.value.apply(this, arguments); } }; } }
测试代码
import { deprecate } from './decorators/index' class TestClass { @deprecate() hello(name: string) { console.log('hello', name); } } const a = new TestClass() a.hello('world')
运行结果:
DEPRECATION TestClass#hello: This function will be removed in future versions. hello world
参考
未完待续
后面和大家一起学习一下 core-decorators
中的源码,学习一下常用的优秀的装饰器设计