• TypeScript学习: 十二、TS中的装饰器


    前言

    装饰器: 装饰器是一种特殊类型声明, 它能够被附加到类声明,方法,属性或者参数上, 可以修改类的行为
    通俗的讲装饰器就是一个方法, 可以注入到类,方法,属性参数上来扩展类,属性,方法,参数功能
    常见的装饰器:属性装饰器,方法装饰器,参数装饰器
    写法: 普通修饰器(无法传参)、装饰器工厂(可以传参)
     

    类修饰器

    装饰器在类声明之前被执行,类装饰器应用于类构造函数,可以用来监视,修改或者代替类定义
     

    类普通装饰器:

    function logClass(params: any) { 
        console.log(params); 
    }
    
    
    @logClass
    class HttpClient{
        constructor() {
    
        }
        getData() {
    
        }
     }

     定义了一个 logClass 类   并且带了一个默认参数  params

    这个 params == HttpClinent   

    function logClass(params: any) {
        console.log(params); 
        params.prototype.httpUrl = "http.xxxxx"; // 通过原型链添加一个 给类添加一个属性
    }
    
    
    @logClass
    class HttpClient{
        constructor() {
    
        }
        getData() {
    
        }
     }
    
    var httpClient = new HttpClient();
    console.log(httpClient.httpUrl);

    运行结果:

     通过 类装饰器添加 方法

    function logClass(params: any) {
        console.log(params); 
        params.prototype.httpUrl = "http.xxxxx"; // 通过原型链添加一个 给类添加一个属性
    
        params.prototype.run = function() {
            console.log("run-app");
        }
    }
    
    @logClass
    class HttpClient{
        constructor() { }
        getData() { }
     }
    
    var httpClient = new HttpClient();
    console.log(httpClient.httpUrl);
    httpClient.run(); // 执行通过装饰器添加的方法

     类装饰器工厂:

    function logClass(params: string) {
        console.log(params); // 传入的参数类型
       return function(tarage:any) { 
            // tarage == HttpClient
       }
    }
    
    @logClass("传入参数")
    class HttpClient{
        constructor() { }
        getData() { }
    }
    
    var httpClient = new HttpClient();

     如果是装饰器工厂必须要写入 参数     @logClass("传入参数")    

    可以根据装饰器传入的参数去实现需要的功能

    function logClass(params: string) {
        console.log(params); // 传入的参数类型
       return function(tarage:any) { 
            // tarage == HttpClient
            tarage.prototype.apiUrl = params; // 通过原型链修改 类的属性
       }
    }
    
    @logClass("http://xxxxxx123")
    class HttpClient{
        constructor() { }
        getData() { }
    }
    
    var httpClient = new HttpClient();
    而且还可以重载 构造函数
    function logClass(target: any) {
        console.log(target);
       return class extends target{
            apiUrl:any="修改后的数据";
            getData() {
                this.apiUrl = this.apiUrl + "---===--";
                console.log(this.apiUrl);
            }
       }
    }
    
    @logClass
    class HttpClient{
        public apiUrl: string | undefined;
        constructor() { 
            this.apiUrl = "我是构造函数里面的apiUrl";
        }
        getData() { 
            console.log(this.apiUrl);
        }
    }
    
    var httpClient = new HttpClient();
    console.log(httpClient.apiUrl)
    httpClient.getData();

     属性装饰器

     属性装饰器表达式会在运行时当作函数调用, 传入2个参数
     * 第一个参数:对于静态成员来说 是类的构造函数, 对于实例化成员是类的原型对象
     * 第二个参数:成员名字
    function logProperty(params: any) {
        console.log("接收传入的参数", params);
        return function(tarage: any, attr: any) {
            console.log("参数一:", tarage); // 对于实例化成员是类的原型对象
            console.log("参数二:", attr);// 成员名字
        }
    }
    
    class HttpClient{
        
        @logProperty("test")
        public url:string|undefined; // 这个属性使用 属性装饰器
        
        constructor() {   }
        getData() {  }
    }

     通过参数一   修改属性值:

    function logProperty(params: any) {
        console.log("接收传入的参数", params);
        return function(tarage: any, attr: any) {
            console.log("参数一:", tarage); // 对于实例化成员是类的原型对象
            console.log("参数二:", attr);// 成员名字
            tarage[attr] = params; // 这里修改属性值
        }
    }
    
    class HttpClient{
        
        @logProperty("test")
        public url:string|undefined; // 这个属性使用 属性装饰器
        
        constructor() {   }
        getData() { 
            console.log( "url属性值", this.url);
         }
    }
    
    var httpClient = new HttpClient();
    httpClient.getData(); // 输出     url属性值 test

    方法装饰器

    它会被应用到方法的 属性描述符上, 可以用来监视,修改或者替换方法定义
     
     * 方法装饰器会在运行时传入3个参数
     *  参数一: 对于静态成员来说是类的构造函数,对于实例成员是类的原型
     *  参数二: 成员名字
     *  参数三: 成员的属性描述符。
    function logMethod(params:any) {
        console.log("调用传入的参数", params)
        return function(tarage:any, methodName: string, desc:any){
            console.log("参数一", tarage); // 实例成员是类的原型
            console.log("参数二", methodName); // 成员名字, 方法名
            console.log("参数三", desc); // 描述
        }
    }
    
    
    
    class HttpClient{
        
        public url:string|undefined;
        
        constructor() {   }
    
        @logMethod("get")
        getData() { // 这个方法使用了 装饰器
            console.log( "url属性值", this.url);
         }
    }

     第一个参数是 实例成员的原型, 可以通过它给原型添加属性或方法

    function logMethod(params:any) {
        console.log("调用传入的参数", params)
        return function(tarage:any, methodName: string, desc:any){
            console.log("参数一", tarage); // 实例成员是类的原型
            console.log("参数二", methodName); // 成员名字, 方法名
            console.log("参数三", desc); // 描述
    
            tarage.api = "我是装饰器添加的 属性";
            tarage.run = function() {
                console.log("我是装饰器添加的方法");
            }
        }
    }
    
    
    
    class HttpClient{
        
        public url:string|undefined;
        
        constructor() {   }
    
        @logMethod("get")
        getData() { // 这个方法使用了 装饰器
            console.log( "url属性值", this.url);
         }
    }
    
    var httpClient = new HttpClient();
    console.log(httpClient.api); // 输出: 我是装饰器添加的 属性
    httpClient.run();  // 输出 我是装饰器添加的方法
    第三个参数是 描述    
    修改装饰器方法, 把装饰器方法里面的所有参数修改为string类型
    function logMethod(params:any) {
        console.log("调用传入的参数", params)
        return function(tarage:any, methodName: string, desc:any){
           
           console.log(desc.value); // 输出 function() { console.log( "url属性值", this.url);}
           var methodFun = desc.value;
           desc.value = function(...args:any) { // 重写 getData 方法
                args = args.map((value:any)=>{
                    return String(value);
                });
                console.log(args);
    
                // 使用对象冒充, 还原之前的函数
                methodFun.apply(this, args); // 执行输出 我是getData方法
           }
        }
    }
    
    class HttpClient{
        
        public url:string|undefined;
        
        constructor() {   }
    
        @logMethod("get")
        getData(...args:any) { // 这个方法使用了 装饰器
            console.log(args);// 输出: Array(2) ["123", "adb"]
            console.log( "我是getData方法");
         }
    }
    
    var httpclient = new HttpClient();
    httpclient.getData(123, "adb");  // 输出: Array(2) ["123", "adb"]

    方法参数装饰器

    参数装饰器表达式会在运行时当作函数调用,可以使用参数装饰器为类的原型
    增加一些元素数据,传入三个参数:

    参数一: 对于静态成员来说是类的构造函数, 对于实例成员是类的原型对象
    参数二:方法名称
    参数三:参数在函数参数列表中的索引
    function logParams(params:any) {
        return function(target:any,methodName:any,paramsIndex:number){
            console.log(params);// 我是装饰器参数
            console.log(target); // Object {getData: , constructor: }
            console.log(methodName); // getData
            console.log(paramsIndex);// 0
        }
    }
    
    class HttpClient{
        public url:string|undefined;
        constructor() {   }
    
        getData(@logParams("我是装饰器参数")uuid:any) { // 这个方法使用了 装饰器
            console.log(uuid);
            console.log( "我是getData方法");
         }
    }
    
    var httpClinet = new HttpClient();
    httpClinet.getData("test");

     注意装饰器的执行顺序:

    属性装饰器>方法装饰器>方法参数装饰器>类装饰器

    如果一个方法有两个装饰器, 那么 后面的装饰器先执行,再到前面的

  • 相关阅读:
    我很喜欢玩游戏,那么我就适合做游戏程序员吗?
    宁可多花1000元租房,也绝不要去挤半小时地铁
    996 盛行的年代,互联网人如何平衡工作和生活 ?
    互联网公司里都有哪些潜规则?
    那些拼命加班的程序员们,后来都怎么样了?
    MongoDB更需要好的模式设计 及 案例赏析
    MongoDB 提升性能的18原则(开发设计阶段)
    关于MongoDB数据库的日志解析
    实现MongoDB读写分离的“读偏好”介绍
    MongoDB分片 在部署和维护管理 中常见事项的总结
  • 原文地址:https://www.cnblogs.com/yangWanSheng/p/15820438.html
Copyright © 2020-2023  润新知