TS裝飾器

一:類(lèi)的裝飾器:是一種與類(lèi)(class)相關(guān)的語(yǔ)法,用來(lái)注釋或修改類(lèi)和類(lèi)方法,裝飾器本身是一個(gè)函數(shù),裝飾器通過(guò)@來(lái)使用.

//注意裝飾器的執(zhí)行時(shí)機(jī),不是在 創(chuàng)建實(shí)例的時(shí)候運(yùn)行,而是在類(lèi)創(chuàng)建的時(shí)候就會(huì)執(zhí)行。
function testDec(constructor: any) {
  console.log("123");
}

@testDec
class Person {}
//裝飾器對(duì)類(lèi)的行為的改變,是代碼編譯時(shí)發(fā)生的,而不是在運(yùn)行時(shí)。這意味著,裝飾器能在編譯階段運(yùn)行代碼。也就是說(shuō),裝飾器本質(zhì)就是編譯時(shí)執(zhí)行的函數(shù)。
//所以:這里類(lèi)Person創(chuàng)建完成即會(huì)打印log:123

多個(gè)類(lèi)的裝飾器同時(shí)使用:

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

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

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

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


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

當(dāng)我們需要對(duì)裝飾器的執(zhí)行進(jìn)行控制的時(shí)候,即有的時(shí)候希望裝飾器執(zhí)行,有的時(shí)候不希望他執(zhí)行,這種時(shí)候需要傳遞參數(shù)來(lái)進(jìn)行控制.

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

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

當(dāng)我們通過(guò)prototype對(duì)類(lèi)進(jìn)行拓展時(shí)存在下面的問(wèn)題,實(shí)例化對(duì)象上不找不到拓展的方法(屬性)

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

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

未解決上圖的問(wèn)題,我們對(duì)代碼進(jìn)行優(yōu)化,用工廠(chǎng)方法對(duì)裝飾器進(jìn)行封裝,返回一個(gè)裝飾過(guò)的類(lèi)

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

二:方法的裝飾器

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

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

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

獲取屬性的值
屬性值6:set
設(shè)置屬性的值

三:訪(fǎng)問(wèn)器的裝飾器

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"; //觸發(fā)set
console.log(book.name); //觸發(fā)get
注意.png

四.屬性的訪(fǎng)問(wèn)器

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

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

五:參數(shù)上的裝飾器

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

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

六.裝飾器的簡(jiǎn)單應(yīng)用

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 不存在

上面的方法能夠成功的捕獲到錯(cuò)誤,但是當(dāng)存在大量的屬性需要try catch時(shí),上面的方式過(guò)于復(fù)雜,復(fù)用性太低.這個(gè)時(shí)候我們可以用裝飾器來(lái)進(jìn)行統(tǒng)一的捕獲錯(cuò)誤的處理.

//為了給出精確的提示,我們用工廠(chǎng)方法進(jìn)行封裝個(gè)
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 不存在
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 寫(xiě)在最前:本文轉(zhuǎn)自掘金[https://juejin.cn/post/7095717238149218317] 前...
    沒(méi)名字的某某人閱讀 1,468評(píng)論 0 2
  • typescript中的裝飾器有很多種,比如類(lèi)裝飾器、方法裝飾器、屬性裝飾器等等,先看看裝飾器的定義吧,下面以類(lèi)裝...
    超人鴨閱讀 1,262評(píng)論 0 9
  • 今天做項(xiàng)目的時(shí)候發(fā)現(xiàn)要用到watch來(lái)監(jiān)聽(tīng),所以就學(xué)習(xí)了watch的裝飾器寫(xiě)法,然后順便把之前用過(guò)的都看了,這里做...
    鄭饞師閱讀 4,162評(píng)論 0 5
  • 前言 兩年前剛學(xué)ts,當(dāng)時(shí)搭了個(gè)簡(jiǎn)單的koa的demo,介紹了如何用裝飾器管理koa的路由:TS裝飾器初體驗(yàn),用裝...
    超人鴨閱讀 1,841評(píng)論 2 5
  • 裝飾器是一種特殊類(lèi)型的聲明,本質(zhì)上就是一個(gè)方法,可以注入到類(lèi)、方法、屬性、參數(shù)上,擴(kuò)展其功能; 常見(jiàn)的裝飾器:類(lèi)裝...
    hellomyshadow閱讀 11,276評(píng)論 3 11

友情鏈接更多精彩內(nèi)容