聊一聊 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;
};
複製代碼

(四)參考連接

(五)其餘連接

微信公衆號:前端雜論函數

相關文章
相關標籤/搜索