一种特殊类型声明, 可以被附加到类声明, 方法, 属性或参数上,可以修改类的行为
启用装饰器
装饰器是一项实验性特性,如果要启用你必须在命令行或tsconfig.json里启用
命令行
tsc --target ES5 --experimentalDecorators
tsconfig.json
{
"compilerOptions": {
"target": "ES5",
"experimentalDecorators": true
}
}
装饰器工厂
若要要定制一个修饰器如何应用到一个声明上,我们得写一个装饰器工厂函数。
function log(params: any) {
return function (target: any) {
console.log(params); // 装饰器所装饰的类-HttpClient
console.log(target); // 装饰器传入的值-'123'
}
}
@log('123')
class HttpClient { }
组合装饰器
一个对象可以添加多个装饰器
执行顺序是依次执行
// 书写在同一行上:
@f @g x
// 书写在多行上:
@f
@g
x
类装饰器
在类声明前被声明, 紧靠类
// 普通装饰器
function logClass(params: any) {
console.log(params); // [Function: HttpClient] 即装饰器注入的类
params.prototype.apiUrl = "xxx"
params.prototype.getApi = () => params.prototype.apiUrl
}
@logClass
class HttpClient {
cosntructor() { }
getData() { }
}
var http: any = new HttpClient()
console.log(http.apiUrl); // xxx
// 装饰器工厂
function logClass(params: string) {
return function (target: any) {
console.log(params); // HttpClient类
console.log(target); // hello
target.prototype.apiUrl = params
}
}
@logClass('hello') // 工厂装饰器必须传参
class HttpClient {
cosntructor() { }
getData() { }
}
var http = new HttpClient()
console.log(http.apiUrl)// hello (但是这里会报错-类型“HttpClient”上不存在属性“apiUrl”)
装饰器也可以重新定义类方法和属性, 即重载类
function logClass(params: any) {
return class extends params {
apiUrl = '修改后'
getData() { // 不重载, 提示缺少属性 "getData",但类型 "HttpClient" 中需要该属性
console.log('装饰器' + this.apiUrl);
}
}
}
@logClass
class HttpClient {
public apiUrl: string | undefined = '我是构造函数apiUrl'
getData() {
console.log('原本' + this.apiUrl);
}
}
var http = new HttpClient()
http.getData() // 装饰器修改后
属性装饰器
会在运行时当作函数被调用,传入两个参数
- 静态成员时类的构造函数, 实例成员时类的原型对象
- 成员的名字
function logProperty(params: any) {
return function (target: any, attr: any) {
console.log(params) // 123-传入的参数
console.log(target); // HttpClient { cosntructor: [Function], getData: [Function] }-类的原型对象HttpClient类
console.log(attr); // apiUrl-apiUrl属性名
target[attr] = params
}
}
class HttpClient {
@logProperty('123')
public apiUrl: string | undefined
cosntructor() {
}
getData() {
console.log(this.apiUrl);
}
}
var http: any = new HttpClient()
console.log(http.apiUrl) // '123'
方法装饰器
放在方法上,用于监视,修改或替换方法的定义
传入三个参数
- 静态成员时类的构造函数, 实例成员时类的原型对象
- 方法的名字
- 方法的属性描述符
function logMethod(params: any) {
return function (target: any, methodName: any, desc: any) {
console.log(params) // 123-传入的参数
console.log(target); // HttpClient { cosntructor: [Function], getData: [Function] }-类的原型对象HttpClient类
console.log(methodName); // getData-方法名
console.log(desc); // {value: [Function], writable: true,enumerable: true,configurable: true}-方法属性
// desc.value 当前方法
const omethod = desc.value
desc.value = function (...args: any[]) {
console.log('这是注释器添加的方法')
// 修改getData的方法
omethod.apply(this, args) // 同时附加原方法
}
}
}
class HttpClient {
public apiUrl: string | undefined
constructor(a: string) {
this.apiUrl = a
}
@logMethod('123')
getData() {
console.log(this.apiUrl);
}
}
const h = new HttpClient('123');
h.getData() // 这是注释器添加的方法 123
参数装饰器
传入三个参数
- 静态成员时类的构造函数, 实例成员时类的原型对象
- 参数的名字
- 参数在函数列表中索引
function logArgument(params: any) {
return function (target: any, Name: any, index: any) {
console.log(params) // 123-传入的参数
console.log(target); // HttpClient { getData: [Function] }-类的原型对象HttpClient类
console.log(Name); // getData-方法名 (这里又和官网解释不同, 期望有大神解答下)
console.log(index); // 0-参数在函数列表中索引
}
}
class HttpClient {
getData(@logArgument('123') apiUrl: string) {
console.log(apiUrl);
}
}
执行顺序
function logArgument(params: any) {
return function (target: any, Name: any, index: any) {
console.log('参数装饰器')
}
}
function logMethod(params: any) {
return function (target: any, methodName: any, desc: any) {
console.log('方法装饰器')
}
}
function logProperty(params: any) {
return function (target: any, attr: any) {
console.log('属性装饰器')
}
}
function logClass(params: string) {
return function (target: any) {
console.log('类装饰器')
}
}
@logClass('Class')
class HttpClient {
@logProperty('Property')
apiUrl: string = 'apiUrl'
@logMethod('Method')
getData(@logArgument('Argument') apiUrl: string) {
console.log(apiUrl);
}
}
// 属性装饰器 => 参数装饰器 => 方法装饰器 => 类装饰器