TS裝飾器

一:類的裝飾器:是一種與類(class)相關的語法,用來注釋或修改類和類方法,裝飾器本身是一個函數,裝飾器通過@來使用.

//注意裝飾器的執行時機,不是在 創建實例的時候運行,而是在類創建的時候就會執行。
function testDec(constructor: any) {
  console.log("123");
}

@testDec
class Person {}
//裝飾器對類的行為的改變,是代碼編譯時發生的,而不是在運行時。這意味著,裝飾器能在編譯階段運行代碼。也就是說,裝飾器本質就是編譯時執行的函數。
//所以:這里類Person創建完成即會打印log:123

多個類的裝飾器同時使用:

//從上至下(從左至右)收集裝飾器,但是執行的時候,先收集的后執行
function testDec(constructor: any) {
  console.log(123);
}

function testDec2(constructor: any) {
  console.log(456);
}

@testDec
@testDec2
class People {}
//456
//123

注意,使用裝飾器時,需要修改ts的配置文件


ts中使用裝飾器注意修改配置文件這兩項.png

當我們需要對裝飾器的執行進行控制的時候,即有的時候希望裝飾器執行,有的時候不希望他執行,這種時候需要傳遞參數來進行控制.

function dec(flag: boolean) {
  return function (constructor: any) {
    if (flag) {
     //我們可以對constructor進行拓展
      return constructor.prototype.getName = () =>{
          console.log("dell");
      }
    } else {
      console.log(222);
    }
  };
}

@dec(true)
class Color {
  constructor(private name: string) {}
}
//log輸出 111

當我們通過prototype對類進行拓展時存在下面的問題,實例化對象上不找不到拓展的方法(屬性)

function testDecorator(constructor: any) {
  constructor.prototype.getName = () => {
    return "lee";
  };
}
@testDecorator
class Test {
  constructor(public name: string) {}
}

const test = new Test("lili");
test.getName();
//可以通過這種方式來解決,但是不符合ts的思想
(test as any).getName();
裝飾過的類上找不到拓展的方法.png

未解決上圖的問題,我們對代碼進行優化,用工廠方法對裝飾器進行封裝,返回一個裝飾過的類

//這里做一層封裝,返回一個裝飾器
function testDecorator() {
  //泛型,一個可以接收多個參數的構造函數
  return function <T extends new (...args: any[]) => any>(constructor: T) {
    return class extends constructor {
      name = "lee";
      getName() {
        return this.name;
      }
    };
  };
}
//獲取裝飾器裝飾過的類
const Test = testDecorator()(
  class {
    constructor(public name: string) {}
  }
);
//此時可以調用getName方法
const test = new Test("lili");
console.log(test.getName());

二:方法的裝飾器

//普通函數的target:類的prototype原型
//靜態函數的target:類的構造函數
function funcDec(target: any, key: string, descriptor: PropertyDescriptor) {
  console.log(target, key);
  //descriptor.writable = false;//設置是否可修改
  descriptor.value= ()=>{
      return "newName"  
  }
}

class Question {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  @funcDec
  getName() {
    return this.name;
  }
}
const question = new Question("問題");
console.log(Question.prototype);
question.getName();
descriptor參考js的Object.defineProperty方法.png

語法:Object.defineProperty(obj,property,descriptor)
參數一:obj
綁定屬性的目標對象
參數二:property
綁定的屬性名
參數三:descriptor
屬性描述(配置),且此參數本身為一個對象
屬性值1:value
設置屬性默認值
屬性值2:writable
設置屬性是否能夠修改
屬性值3:enumerable
設置屬性是否可以枚舉,即是否允許遍歷
屬性值4:configurable
設置屬性是否可以刪除或編輯屬性值5:get

獲取屬性的值
屬性值6:set
設置屬性的值

三:訪問器的裝飾器

function visitDec(target: any, key: string, descriptor: PropertyDescriptor) {
  //descriptor.configurable = false;
}

class Book {
  private _name: string;
  constructor(name: string) {
    this._name = name;
  }
  @visitDec
  get name() {
    return this._name;
  }

  set name(name: string) {
    this._name = name;
  }
}

const book = new Book("三體");
book.name = "三體2"; //觸發set
console.log(book.name); //觸發get
注意.png

四.屬性的訪問器

//屬性訪問器只能接收兩個參數,需要手動的返回descriptor
function nameDec(target: any, key: string): any {
//這里修改的并不是實例上的name,而是原型上的name
  target[key] = "第一行代碼";
  const descriptor: PropertyDescriptor = {
    writable: true,
  };
  return descriptor;
}
//類中的屬性name是放在實例上的
class Book {
  @nameDec
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}

const book = new Book("三體");
book.name = "三體2";
console.log(book.name);

五:參數上的裝飾器

//類的原型 方法名   參數所在的位置
function paramDec(target: any, key: string, paramsIndex: number) {
  console.log(target, key, paramsIndex);
}

class Book {
  getBookName(@paramDec name: string, price: number) {
    return `書名:${name},價格是:${price}`;
  }
}
//Book {} getBookName 0

六.裝飾器的簡單應用

const BookInfo: any = undefined;

class Book {
  getBookName() {
    try {
      return BookInfo.name;
    } catch (e) {
      console.log("BookInfo -- name 不存在");
    }
  }
  getBookPrice() {
    try {
      return BookInfo.price;
    } catch (e) {
      console.log("BookInfo --- price 不存在");
    }
  }
}

const book = new Book();
book.getBookName(); //BookInfo -- name 不存在
book.getBookPrice(); //BookInfo --- price 不存在

上面的方法能夠成功的捕獲到錯誤,但是當存在大量的屬性需要try catch時,上面的方式過于復雜,復用性太低.這個時候我們可以用裝飾器來進行統一的捕獲錯誤的處理.

//為了給出精確的提示,我們用工廠方法進行封裝個
function catchError(msg: string) {
  return function (target: any, key: string, descriptor: PropertyDescriptor) {
    let fn = descriptor.value;
    descriptor.value = () => {
      try {
        fn();
      } catch (e) {
        console.log(msg);
      }
    };
  };
}
const BookInfo: any = undefined;

class Book {
  @catchError("BookInfo -- name 不存在")
  getBookName() {
    return BookInfo.name;
  }
  @catchError("BookInfo -- price 不存在")
  getBookPrice() {
    return BookInfo.price;
  }
}

const book = new Book();
book.getBookName(); //BookInfo -- name 不存在
book.getBookPrice(); //BookInfo --- price 不存在
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,460評論 6 538
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,067評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,467評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,468評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,184評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,582評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,616評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,794評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,343評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,096評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,291評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,863評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,513評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,941評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,190評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,026評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,253評論 2 375

推薦閱讀更多精彩內容

  • 寫在最前:本文轉自掘金[https://juejin.cn/post/7095717238149218317] 前...
    沒名字的某某人閱讀 1,173評論 0 2
  • typescript中的裝飾器有很多種,比如類裝飾器、方法裝飾器、屬性裝飾器等等,先看看裝飾器的定義吧,下面以類裝...
    超人鴨閱讀 1,176評論 0 9
  • 今天做項目的時候發現要用到watch來監聽,所以就學習了watch的裝飾器寫法,然后順便把之前用過的都看了,這里做...
    鄭饞師閱讀 4,036評論 0 5
  • 前言 兩年前剛學ts,當時搭了個簡單的koa的demo,介紹了如何用裝飾器管理koa的路由:TS裝飾器初體驗,用裝...
    超人鴨閱讀 1,653評論 2 5
  • 裝飾器是一種特殊類型的聲明,本質上就是一個方法,可以注入到類、方法、屬性、參數上,擴展其功能; 常見的裝飾器:類裝...
    hellomyshadow閱讀 11,174評論 3 11