TypeScript(装饰器)

什么是装饰器?

  装饰器:装饰器是一种特殊类型的声明,它能够附加到类声明、方法、属性和参数上,可以修改类的行为。

  通俗的讲装饰器就是一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能。

  常见的装饰器:类装饰器,属性装饰器,方法装饰器,参数装饰器。

  装饰器的写法:普通装饰器(无法传参),装饰器工厂(可传参)

  装饰器是过去几年中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*/

 

posted @ 2020-11-21 19:57  惊蛰丶  阅读(266)  评论(0编辑  收藏  举报