聊一聊 TypeScript 的装饰器

装饰器是一种特殊类型的声明,它可以被附加到类声明,方法, 访问符,属性或参数上。 装饰器使用 @expression 这种形式,expression 求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息作为参数传入。html

(一)使用

注意:装饰器是一项实验性特性,在将来的版本中可能会发生改变。前端

在 TypeScript 中装饰器还属于实验性语法,你必须在命令行或 tsconfig.json 里启用 experimentalDecorators 编译器选项:es6

  • 命令行:
tsc --target ES5 --experimentalDecorators
复制代码
  • tsconfig.json:
{
    "compilerOptions": {
        "target": "ES5",
        "experimentalDecorators": true
    }
}
复制代码

一、装饰类

针对类的修饰,会接受一个参数即类对象自己,下文经过对类添加静态属性实现。express

@testable
class Demo{}

function testable(target) {
    target.isTestable=true
}

console.log(Demo.isTestable) // true
复制代码

二、 装饰类方法

针对类方法修饰,函数会接收3个参数:json

  • 一、target:当前对象的原型
  • 二、key:当前方法名
  • 三、descriptor:方法的属性描述符
// descriptor对象原来的值以下
// {
// value: specifiedFunction,
// enumerable: false,
// configurable: true,
// writable: true
// };

// target:在方法中的target指向类的prototype
function readonly(target,key,descriptor){
    descriptor.writable=false
    return descriptor
}

class Demo {
    @readonly
    print(){console.log(`a:${this.a}`)}
}
复制代码

三、 装饰类属性

针对类属性装饰器,函数会接收2个参数:微信

  • 一、target:当前对象的原型
  • 二、key:当前属性名
function set(value: string) {
    return function (target: any, propertyName: string) {
        target[propertyName] = value;
    }
}

class Demo {
    @set("hello world") greeting: string;
}

console.log(new Demo().greeting);// hello world
复制代码

(二)装饰器的执行顺序

多个修饰器的执行顺序是由外向内进入;再由内向外执行。app

class Demo {
    @log(1)
    @log(2)
    method(){}
}

function log(id){
    console.log('id is ',id)
    return (target,property,descriptor)=>console.log('executed',id)
}

// 控制台输出
// id is 1
// id is 2
// executed 2
// executed 1
复制代码

(三)实现几个简单的装饰器

本文用到的个小方法async

// 返回空对象
export const noop = () => { };

/** * 判断对象类型 * @param [obj: any] 参数对象 * @returns String */
export function isType(obj) {
  return Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/, '$1').toLowerCase();
}
复制代码

一、装饰器实现异常捕获

/** * 用于捕获Error * @tips 请注意,参数params中的回调函数不能使用箭头函数,不然将会引发上下文变更 * @param [Object | Function] params 接收异常捕获回调,非必选参数 * @param [params.callback] callback 默认接收Function类型的参数,或者接收 Object 对象中的callback方法 * @returns descriptor */
export const DRCatchError = (params: any = noop) => (target, key, descriptor) => {
    const original = descriptor.value;
    let errorHandler: any = null;
    if (isType(params) === 'function') {
        errorHandler = params;
    } else if (isType(params) === 'object') {
        errorHandler = params.callback;
    }
    descriptor.value = async function () {
        try {
            await original.apply(this, arguments);
        } catch (error) {
            let resp = (error && error.response) || {};
            let errorData = resp.data || error.message;
            if (typeof errorHandler === 'function') {
                errorHandler.call(this, errorData);
            } else {
                console.error(error);
            }
        }
    };
    return descriptor;
};
复制代码

二、装饰器实现防抖

/** * 用于操做方法防抖 * @param [wait: number] 延时ms * @param [immediate: boolean] 当即执行 * @returns descriptor */
export const DRDebounce = (wait: number, immediate: boolean = false) => (target, key, descriptor) => {
   let timeout: any;
   const original = descriptor.value;
   descriptor.value = function () {
       let later = () => {
           timeout = null;
           if (!immediate) {
               original.apply(this, arguments);
           }
       };
       let callNow = immediate && !timeout;
       clearTimeout(timeout);
       timeout = setTimeout(later, wait);
       if (callNow) {
           original.apply(this, arguments);
       }
   };
   return descriptor;
};
复制代码

三、装饰器实现节流

/** * 用于操做函数节流 * @param [delay: number] 延时ms */
export const DRThrottle = (delay: number) => (target, key, descriptor) => {
   let last: any;
   let deferTimer: any;
   const original = descriptor.value;
   descriptor.value = function() {
       let now = +new Date();
       if (last && now < last + delay) {
           clearTimeout(deferTimer);
           deferTimer = setTimeout(() => {
               last = now;
               original.apply(this, arguments);
           }, delay);
       }else {
           last = now;
           original.apply(this, arguments);
       }
   };
   return descriptor;
};
复制代码

(四)参考连接

(五)其余连接

微信公众号:前端杂论函数

相关文章
相关标签/搜索