什么是装饰器?
装饰器:装饰器是一种特殊类型的声明,它能够附加到类声明、方法、属性和参数上,可以修改类的行为。
通俗的讲装饰器就是一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能。
常见的装饰器:类装饰器,属性装饰器,方法装饰器,参数装饰器。
装饰器的写法:普通装饰器(无法传参),装饰器工厂(可传参)
装饰器是过去几年中js最伟大的成就之一,已是ES7的标准特性之一
1.类装饰器:类装饰器在紧靠类声明前声明。类装饰器应用于类构造函数,可以用来监视、修改或替换类定义,传入一个参数
修改tsconfig.json,"experimentalDecorators": true,
1.1类装饰器(普通装饰器)
// 普通装饰器(无法传参数)
function logClass(params:any){
console.log(params) // "ƒ HttpClient() {}",params就是当前类,可以扩展类的属性和方法
params.prototype.apiUrl='htttp://xxx' // 扩展类
params.prototype.run=function(){ // 扩展方法
console.log('run方法')
}
}
@logClass // 紧靠类声明装饰器,后面不加分号
class HttpClient{
constructor(){
}
getData(){
}
}
let http:any = new HttpClient();
console.log(http.apiUrl); // "htttp://xxx"
http.run() // "run方法"
1.2类装饰器(装饰器工厂)
// 装饰器工厂(可传参)
function logClass(params:string){
return function(target:any){
console.log(params) // "hello",获取到传入的参数
console.log(target) // "ƒ HttpClient() {}"
target.prototype.uriApi="http://xxx" // 扩展属性
target.prototype.run=function(){ // 扩展方法
console.log('扩展方法run')
}
}
}
@logClass('hello') // 紧靠类声明装饰器,后面不加分号
class HttpClient{
constructor(){
}
getData(){
}
}
let http:any = new HttpClient();
console.log(http.uriApi) // "http://xxx"
http.run() // '扩展方法run'
1.3修改、替换类的属性和方法
// 重载构造函数
function logClass(target:any){
return class extends target{ // target就是当前类
urlApi:any="/api" // 替换类里的属性和方法
getData(){
this.urlApi=this.urlApi+"/index"
console.log(this.urlApi)
}
}
}
@logClass
class HttpClient{
public urlApi:string |undefined
constructor(){
this.urlApi=="/admin"
}
getData(){
console.log(this.urlApi)
}
}
let http:any = new HttpClient();
console.log(http.urlApi) // "/api"
http.getData() // "/api/index"
2.属性装饰器:接收两个参数
第一个参数:对静态成员来说是类的构造函数,对实例成员来说是类的原型对象
第二个参数:成员的名字
// 属性装饰器
function logProprety(params:any){
return function(target:any,attr:any){
console.log(target) // {getData: ƒ, constructor: ƒ},类的原型对象,这里的target相当于上面类装饰器的target.prototype
console.log(attr) // urlApi,属性名
target[attr] = params
}
}
class HttpClient{
@logProprety('/api')
public urlApi:string |undefined
constructor(){
}
getData(){
console.log(this.urlApi)
}
}
let http:any = new HttpClient();
console.log(http.urlApi) // /api
http.getData() // /api
3.方法装饰器:用于方法的属性描述符上,可以监视、修改、替换方法定义;接收三个参数
第一个参数:类的原型对象
第二个参数:成员的名字
第三个参数:成员的属性描述符
// 方法装饰器
function logMethod(params:any){
return function(target:any,methodName:any,desc:any){
console.log(params) // 456,传入的参数
console.log(target) // {getData: ƒ, constructor: ƒ},原型对象
console.log(methodName) // getData,方法名
console.log(desc) // {writable: true, enumerable: true, configurable: true, value: ƒ} ,方法描述
console.log(desc.value) // ƒ () {console.log(this.urlApi);}
target.api='xxx' // 扩展属性和方法
target.run=function(){
console.log('run方法')
}
target.getData=function(){
console.log('getData方法')
}
// 修改装饰器的方法,把装饰器方法里面传入的所有参数改为string类型
// 先保存当前方法
let onmethod=desc.value
desc.value=function(...args:any[]){ // 接收参数
args=args.map(item=>{
return String(item)
})
console.log(args) // ["123", "abc"],替换了原方法
onmethod.apply(this,args) // /api,执行原方法
}
}
}
class HttpClient{
public urlApi:string |undefined
constructor(){
this.urlApi='/api'
}
@logMethod('456')
getData(){
console.log(this.urlApi)
}
}
let http:any = new HttpClient();
http.getData(123,'abc')
4.方法参数装饰器:运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元素数据;接收三个参数
第一个参数:对静态成员来说是类的构造函数,对实例成员来说是类的原型对象
第二个参数:方法的名字
第三个参数:参数在函数列表中的索引
// 参数装饰器
function logParams(params:any){
return function(target:any,methodName:any,paramsIndex:any){
console.log(params) // xxx,传入的参数
console.log(target) // {getData: ƒ, constructor: ƒ},原型对象
console.log(methodName) // getData,方法名称
console.log(paramsIndex) // 0,索引
target.apiUrl = params
}
}
class HttpClient{
public urlApi:string | undefined
constructor(){
}
getData(@logParams('xxx') uuid:any){
}
}
let http:any = new HttpClient();
console.log(http.apiUrl) // xxx
装饰器执行顺序
最先执行属性装饰器,最后执行类装饰器
多个同样的装饰器则先执行后面的装饰器
function logClass1(params?:string){
return function(target:any){
console.log('类装饰器1')
}
}
function logClass2(params?:string){
return function(target:any){
console.log('类装饰器2')
}
}
function logProprety(params?:any){
return function(target:any,attr:any){
console.log('属性装饰器')
}
}
function logMethod(params?:any){
return function(target:any,methodName:any,desc:any){
console.log('方法装饰器')
}
}
function logParams1(params?:any){
return function(target:any,methodName:any,paramsIndex:any){
console.log('方法参数装饰器1')
}
}
function logParams2(params?:any){
return function(target:any,methodName:any,paramsIndex:any){
console.log('方法参数装饰器2')
}
}
@logClass1()
@logClass2()
class HttpClient{
@logProprety()
public urlApi:string | undefined
constructor(){
}
@logMethod()
getData(@logParams1() uuid1:any,@logParams2() uuid2:any){
}
}
/*打印结果:
属性装饰器
方法参数装饰器2
方法参数装饰器1
方法装饰器
类装饰器2
类装饰器1*/