<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      TypeScript(八)裝飾器

      目錄

      前言

      定義

      類裝飾器

      基本用法

      操作方式

      操作類的原型

      類繼承操作

      方法裝飾器

      屬性裝飾器

      存取器裝飾器

      參數(shù)裝飾器

      基本用法

      參數(shù)過(guò)濾器

      元數(shù)據(jù)函數(shù)實(shí)現(xiàn)

      參數(shù)過(guò)濾

      效果實(shí)踐

      裝飾器優(yōu)先級(jí)

      相同裝飾器

      不同裝飾器

      裝飾器工廠

      hooks與class兼容

      結(jié)語(yǔ)

      相關(guān)文章


      前言

      本文收錄于TypeScript知識(shí)總結(jié)系列文章,歡迎指正! 

      程序遵循開(kāi)放封閉原則,即在設(shè)計(jì)和編寫軟件時(shí)應(yīng)該盡量避免對(duì)原有代碼進(jìn)行修改,而是通過(guò)添加新的代碼來(lái)擴(kuò)展軟件的功能。

      在日常開(kāi)發(fā)中不知你有沒(méi)有遇到以下情況,我們封裝了一個(gè)Request模塊,現(xiàn)在需要對(duì)請(qǐng)求進(jìn)行攔截,訪問(wèn)請(qǐng)求參數(shù),此時(shí)我們可以通過(guò)裝飾器針對(duì)請(qǐng)求函數(shù)或者請(qǐng)求類進(jìn)行訪問(wèn),獲取參數(shù)并解析

      定義

      在TS中,裝飾器是一種特殊類型的聲明。可以附加到類、方法、屬性或參數(shù)上用于修改類的行為或?qū)傩浴?/strong>

      在面向?qū)ο缶幊讨校袝r(shí)需要對(duì)類的行為和功能做出修改,直接修改類的內(nèi)部可能會(huì)使成本升高,或出現(xiàn)其他問(wèn)題;此時(shí)可以使用裝飾器來(lái)修改類,在保證類內(nèi)部結(jié)構(gòu)與功能不變的前提下對(duì)數(shù)據(jù)或行為進(jìn)行迭代

      TS中裝飾器可以分為類裝飾器、方法裝飾器、屬性裝飾器和參數(shù)裝飾器。

      tips:使用裝飾器前需要在tsconfig中開(kāi)啟experimentalDecorators屬性

      類裝飾器

      類裝飾器是應(yīng)用于類的構(gòu)造函數(shù)的函數(shù),它可以用來(lái)修改類的行為。類裝飾器可以有一個(gè)參數(shù),即類的構(gòu)造函數(shù),通過(guò)這個(gè)參數(shù)我們可以對(duì)類的行為進(jìn)行修改。

      基本用法

      類裝飾器的語(yǔ)法是在一個(gè)普通的函數(shù)名前面加上@符號(hào),后面緊跟著要裝飾的類的聲明,如:

      const nameDecorator = (constructor: typeof Animal) => {
          console.log(constructor.prototype.name)// undefined
      }
      @nameDecorator
      class Animal {
          name: string = "阿黃"
          constructor() {
              console.log(this.name);// 阿黃
          }
      }
      new Animal()
      

      在上述代碼中,我使用decorator獲取Animal類的name屬性,發(fā)現(xiàn)獲取的是未定義,而在構(gòu)造函數(shù)中卻可以獲取,原因是類的裝飾器是在類定義時(shí)對(duì)類進(jìn)行操作的,而屬性及函數(shù)的初始化是當(dāng)類實(shí)例化時(shí)進(jìn)行的,所以獲取不到name的值

      操作方式

      通過(guò)類裝飾器操作類的方式有兩種:操作類的原型和類的繼承

      操作類的原型

      ES6之前的類是通過(guò)構(gòu)造函數(shù)實(shí)現(xiàn)的,其原型prototype屬性是存在的,所以我們?cè)趯?duì)類進(jìn)行操作時(shí)可以使用修改原型的方式

      type IAnimal = {
          name?: string
          getName?: () => string
      }
      const nameDecorator = (constructor: Function) => {
          const _this = constructor.prototype // 模擬類內(nèi)部環(huán)境
          _this.name = "阿黃"
          _this.getName = () => {
              return _this.name
          }
      }
      @nameDecorator
      class Animal implements IAnimal { }
      const animal: IAnimal = new Animal()
      console.log(animal.getName()) // 阿黃
      

      上面代碼實(shí)現(xiàn)了對(duì)類中name屬性初始化以及實(shí)現(xiàn)了類的getName方法,在ES5中如何實(shí)現(xiàn)類的重寫?看看下面代碼對(duì)裝飾器的修改:

      const nameDecorator = (constructor: IAnimalProto) => {
          const _this = constructor.prototype // 模擬類內(nèi)部環(huán)境
          _this.name = "阿黃"
          return class extends constructor {
              getName = () => {
                  return "名字:" + _this.name
              }
          }
      }

      tips:如果通過(guò)這種方式無(wú)法修改類的屬性或方法,可以把tsconfig中target屬性調(diào)整為ES5,兼容低版本瀏覽器,此時(shí)類是通過(guò)構(gòu)造函數(shù)實(shí)現(xiàn)的

      類繼承操作

      ES6中的類語(yǔ)法糖中沒(méi)有prototype屬性,所以我們可以使用繼承的方式實(shí)現(xiàn)上面的代碼,并使用重寫的方式修改類中的同名函數(shù)

      type IAnimal = {
          name?: string
          getName?: () => string
      }
      type IAnimalProto = {
          new(): Animal
      } & IAnimal
      const nameDecorator = (constructor: IAnimalProto) => {
          return class extends constructor {
              constructor(public name = "阿黃") {
                  super()
              }
              getName() {// 重寫類中的函數(shù)
                  return "姓名:" + this.name
              }
          }
      }
      @nameDecorator
      class Animal implements IAnimal {
          name?: string;
          getName() {
              return this.name
          }
      }
      const animal: IAnimal = new Animal()
      console.log(animal.getName()) // 姓名:阿黃

      方法裝飾器

      方法裝飾器是應(yīng)用于類方法的函數(shù),它可以用來(lái)修改方法的行為。方法裝飾器可以接收三個(gè)參數(shù),分別是目標(biāo)類的原型對(duì)象(若裝飾的是靜態(tài)方法,則指的是類本身)、方法名稱和方法描述符。

      需要注意的是構(gòu)造函數(shù)不是類的方法,所以方法裝飾器不能直接用于裝飾構(gòu)造函數(shù)

      tips:在類中使用方法裝飾器需要避免箭頭函數(shù)的出現(xiàn),因?yàn)榧^函數(shù)的this指向它定義的環(huán)境,而不是實(shí)例對(duì)象,這導(dǎo)致了它無(wú)法獲取到類的屬性和方法

      下面是一個(gè)案例

      
      const nameDecorator = (target: Animal, key: string, descriptor: PropertyDescriptor): PropertyDescriptor => {
          console.log(target); // { setName: [Function (anonymous)] }
          console.log(key); // setName
          console.log(descriptor); 
          //   {
          //     value: [Function (anonymous)],
          //     writable: true,
          //     enumerable: true,
          //     configurable: true
          //   }
          return descriptor
      }
      
      class Animal {
          name: string
          @nameDecorator
          setName(name: string) {
              this.name = name
          }
      }
      const animal = new Animal()
      animal.setName("阿黃")
      console.log(animal.name); // 阿黃
      
      

      其中target表示當(dāng)前函數(shù)所在的類,key一般指函數(shù)名,descriptor指當(dāng)前函數(shù)對(duì)象的描述符

      基于上面的代碼我們可以重寫一下類中的setName函數(shù):

      const nameDecorator = (target: Animal, _: string, descriptor: PropertyDescriptor): PropertyDescriptor => {
          descriptor.value = (name: string) => {
              target.name = "名字:" + name
          }
          return descriptor
      }

      屬性裝飾器

      屬性裝飾器應(yīng)用于類屬性的函數(shù),它可以用來(lái)修改屬性的行為或攔截屬性的定義和描述符的訪問(wèn),但是不能修改屬性值。屬性裝飾器可以接收兩個(gè)參數(shù),分別是目標(biāo)類的原型對(duì)象(若裝飾的是靜態(tài)屬性,則指的是類本身)和屬性名稱。

      與方法裝飾器不同屬性裝飾器不會(huì)返回descriptor這個(gè)參數(shù),也就是無(wú)法獲取到屬性的描述

      const nameDecorator = (target: Animal, key: string) => {
          target[key] = '阿黃'
      }
      class Animal {
          @nameDecorator
          name: string
          setName(name: string) {
              this.name = name
          }
      }
      const animal = new Animal()
      console.log(animal.name);// 阿黃
      animal.setName("小黑")
      console.log(animal.name);// 小黑

      tips:為什么無(wú)法使用屬性裝飾器給屬性定義初始值? 此時(shí)可以檢查一下你的tsconfig.json里面的配置target是不是設(shè)置成ES2021以后(如:ESNext,ES2022,ES2023等),在ES2022前在TS中聲明了屬性成員會(huì)在JS編譯成第一張圖,ES2022及以后顯示的是第二張圖

      解決方法:參考這個(gè)

      我們可以將屬性聲明修改為環(huán)境聲明(declare,在后續(xù)文章會(huì)說(shuō)到),或者使用舊版本的target配置

      class Animal {
          @nameDecorator
          declare name: string
          setName(name: string) {
              this.name = name
          }
      }

      存取器裝飾器

      存取器裝飾器是一種特殊類型的裝飾器,它可以被用來(lái)裝飾類中的存取器屬性。它的使用方式與方法裝飾器相同。但是與方法裝飾器不同的是存取器descriptor參數(shù)中沒(méi)有value值,但是我們可以通過(guò)修改descriptor來(lái)重寫getter和setter方法

      const nameDecorator = (_: any, __: string, descriptor: PropertyDescriptor) => {
          const __getter = descriptor.get;
          descriptor.get = function () {// 必須使用function,使用箭頭函數(shù)獲取不到this
              const value = __getter?.call(this);// 運(yùn)行g(shù)et獲取存取器屬性
              return "名字:" + value;
          };
      }
      class Animal {
          constructor(private _name: string) { }
          @nameDecorator
          get name() {
              return this._name
          }
      }
      const animal = new Animal("阿黃")
      console.log(animal.name);

      參數(shù)裝飾器

      參數(shù)裝飾器是應(yīng)用于類構(gòu)造函數(shù)或方法參數(shù)的函數(shù),它可以用來(lái)獲取參數(shù)位置。參數(shù)裝飾器可以接收三個(gè)參數(shù),分別是目標(biāo)類的原型對(duì)象(若裝飾的是靜態(tài)方法,則指的是類本身)、方法名稱和參數(shù)索引(第幾個(gè)參數(shù),從0開(kāi)始)。

      基本用法

      參數(shù)裝飾器是一個(gè)函數(shù),它可以被應(yīng)用到類的構(gòu)造函數(shù)、方法的參數(shù)上,它無(wú)法應(yīng)用在訪問(wèn)器(getter 和 setter)的參數(shù)。

      const nameDecorator = (target: any, key: string, parameterIndex: number) => {
          const name = target.name ?? target.constructor.name
          console.log(`${name}中的${key ?? '構(gòu)造函數(shù)'}第${parameterIndex}個(gè)參數(shù)`);
      }
      class Animal {
          constructor(public _name: string) { }
          setName(@nameDecorator name: string) {
              this._name = name
          }
      }
      new Animal("阿黃")

      參數(shù)裝飾器雖然無(wú)法直接獲取或者修改參數(shù),但是可以將參數(shù)的位置標(biāo)識(shí)出來(lái),與元數(shù)據(jù)(reflect-metadata庫(kù)),以及方法裝飾器配合達(dá)到過(guò)濾參數(shù)的目的

      參數(shù)過(guò)濾器

      下面我們借助一個(gè)簡(jiǎn)單的反射元數(shù)據(jù)操作實(shí)現(xiàn)一個(gè)參數(shù)過(guò)濾器

      元數(shù)據(jù)函數(shù)實(shí)現(xiàn)

      type IKey = string | symbol | number
      const getReflect = () => Reflect ?? Object
      const __Reflect = getReflect()
      
      const defineMeta = (target: any, key: IKey, metadataKey: IKey, descriptor: PropertyDescriptor): void => {
          __Reflect.defineProperty(target[key], metadataKey, descriptor)
      }
      
      const getMeta = (target: any, key: IKey, metadataKey: IKey,): PropertyDescriptor => {
          return __Reflect.getOwnPropertyDescriptor(target[key], metadataKey)
      }

      參數(shù)過(guò)濾

      下面我們實(shí)現(xiàn)一個(gè)參數(shù)過(guò)濾,如果name等于阿黃,則中斷函數(shù)執(zhí)行并跳出

      // 存儲(chǔ)參數(shù)的索引
      const saveMeta2Arr = (target: any, key: string, parameterIndex: number, keyWord: string) => {
          const paramsList = getMeta(target, key, keyWord)?.value ?? []
          paramsList.push(parameterIndex)
          defineMeta(target, key, keyWord, { value: paramsList })
          return paramsList
      }
      // 參數(shù)裝飾器
      const paramsDecorator = (target: any, key: string, parameterIndex: number) => {
          const paramsList: string[] = saveMeta2Arr(target, key, parameterIndex, 'list:params')// 參數(shù)列表
          defineMeta(target, key, 'filter:params', {
              value: (...args) => {
                  if (!!!args.length) return void 0 // 沒(méi)傳參數(shù)默認(rèn)跳過(guò)參數(shù)校驗(yàn)
                  return paramsList.filter(it => args[it] === "阿黃").length > 0// 我的校驗(yàn)規(guī)則是參數(shù)等于阿黃就跳出函數(shù),這個(gè)可以自行修改
              }
          })
      }
      // 函數(shù)裝飾器
      const methodDecorator = (target: Animal, key: string, descriptor: PropertyDescriptor) => {
          const fn = getMeta(target, key, 'filter:params').value // 獲取參數(shù)裝飾器的回調(diào)函數(shù)
          const method = descriptor.value
          descriptor.value = function (...args) {
              if (fn(...args)) return console.error("跳出了函數(shù)");// 過(guò)濾操作
              method.apply(this, args)
          }
      }
      class Animal {
          constructor(public name?: string) { }
          @methodDecorator
          setInfo(@paramsDecorator name?: string) {
              console.log("執(zhí)行了函數(shù)");
              this.name = name
          }
      }
      
      

      效果實(shí)踐

      實(shí)現(xiàn)完成我們實(shí)例化一下類試試,可以看到,此時(shí)由于我們傳入的參數(shù)是阿黃,所以setName函數(shù)未執(zhí)行

      const animal = new Animal()
      animal.setInfo("阿黃")
      console.log(animal.name);
      // 跳出了函數(shù)
      // undefined

      下面我們修改一下,傳入一個(gè)參數(shù):小黑

      const animal = new Animal()
      animal.setInfo("小黑")
      console.log(animal.name);
      // 執(zhí)行了函數(shù)
      // 小黑

      上述代碼執(zhí)行了setName并且將name賦值了小黑

      裝飾器優(yōu)先級(jí)

      下面說(shuō)說(shuō)當(dāng)多個(gè)裝飾器作用于同一個(gè)目標(biāo)時(shí),它們執(zhí)行的順序和影響的優(yōu)先級(jí)是怎樣的

      相同裝飾器

      同一種裝飾器的執(zhí)行順序是從下往上,理解為就近原則,近的先執(zhí)行

      const decorator1 = (...args: any[]) => {
          console.log(1)
      }
      const decorator2 = (...args: any[]) => {
          console.log(2)
      }
      const decorator3 = (...args: any[]) => {
          console.log(3)
      }
      @decorator1
      @decorator2
      @decorator3
      class Animal { }
      new Animal()
      // 輸出3 2 1

      不同裝飾器

      不同裝飾器遵循:參數(shù)>函數(shù)=屬性=存取器>類,參數(shù)優(yōu)先級(jí)最高,類最后執(zhí)行。何以見(jiàn)得?

      const decorator1 = (...args: any[]) => {
          console.log(1)
      }
      const decorator2 = (...args: any[]) => {
          console.log(2)
      }
      const decorator3 = (...args: any[]) => {
          console.log(3)
      }
      const decorator4 = (...args: any[]) => {
          console.log(4)
      }
      const decorator5 = (...args: any[]) => {
          console.log(5)
      }
      @decorator1
      class Animal {
          @decorator2
          _name: string
          @decorator3
          get name() {
              return this._name
          }
          @decorator4
          setName(@decorator5 name: string) {
              this._name = name
          }
      }
      new Animal()

      上面的代碼輸出2 3 5 4 1;我們換個(gè)順序,把屬性,函數(shù),存取器調(diào)換位置再試試

      @decorator1
      class Animal {
          @decorator4
          setName(@decorator5 name: string) {
              this._name = name
          }
          @decorator3
          get name() {
              return this._name
          }
          @decorator2
          _name: string
      }

      輸出5 4 3 2 1。可見(jiàn)屬性,函數(shù),存取器優(yōu)先級(jí)在同級(jí),參數(shù)更高,類更低

      裝飾器工廠

      使用類裝飾器和方法裝飾器時(shí),我們難免會(huì)遇到參數(shù)傳遞的問(wèn)題,每個(gè)裝飾器都有可以復(fù)用的可能,為了使代碼高可用,我們可以嘗試使用高階函數(shù)實(shí)現(xiàn)一個(gè)工廠,外部函數(shù)接收參數(shù),函數(shù)返回裝飾器

      type IAnimal = {
          name?: string
      }
      type IAnimalProto = {
          new(name: string): Animal
      } & IAnimal
      
      const nameDecorator = (name: string) => (constructor: IAnimalProto) => {
          return class extends constructor {
              constructor() {
                  super(name)
              }
          }
      }
      @nameDecorator("阿黃")
      class Animal implements IAnimal {
          constructor(public name?: string) { }
      }
      const animal: IAnimal = new Animal()
      console.log(animal.name);
      

      上述代碼中我們實(shí)現(xiàn)了使用裝飾器工廠給Animal類的屬性name賦予默認(rèn)值的功能

      hooks與class兼容

      在實(shí)際開(kāi)發(fā)中,如果是使用react開(kāi)發(fā)應(yīng)該會(huì)遇到這樣的問(wèn)題,使用類開(kāi)發(fā)的組件裝飾器寫法是這樣

      @EnhanceConnect((state: any) => ({
        global: state['@global'].data
      }))
      export class MyComponent {
        constructor(props: IProps) {
          // ...
        }
      }

      如何轉(zhuǎn)換成hooks寫法?

      export const MyComponent = EnhanceConnect((state: any) => ({
        global: state['@global'].data
      }))((props: IProps) => {
        useEffect(() => {
          // ...
        })
      })

      結(jié)語(yǔ)

      本文詳細(xì)講述了類裝飾器,方法裝飾器,屬性裝飾器,存取器裝飾器,參數(shù)裝飾器這五種裝飾器的基本用法及注意事項(xiàng);此外還針對(duì)裝飾器優(yōu)先級(jí)進(jìn)行了排序及證明;最后介紹了裝飾器工廠即裝飾器的實(shí)際應(yīng)用場(chǎng)景兼容方法。

      感謝你的閱讀,希望文章能對(duì)你有幫助,有任何問(wèn)題歡迎留言私信,請(qǐng)別忘了給作者點(diǎn)個(gè)贊哦,感謝~

      相關(guān)文章

      Decorators - TypeScript 中文手冊(cè)

      裝飾器與混合對(duì)象 - TypeScript 精通指南

      posted @ 2023-03-27 16:25  阿宇的編程之旅  閱讀(517)  評(píng)論(0)    收藏  舉報(bào)  來(lái)源
      主站蜘蛛池模板: 综合人妻久久一区二区精品| 亚洲综合一区二区国产精品 | 国产精品99中文字幕| 四虎永久精品免费视频| 国产精品永久久久久久久久久| 人妻少妇偷人精品一区| 国产午夜亚洲精品国产成人| 午夜性色一区二区三区不卡视频| 久久久国产一区二区三区四区小说| 亚洲乱熟女一区二区三区| 人妻少妇精品中文字幕| 余江县| 国产精品区一二三四久久| 国内精品久久人妻无码妲| 国产精品嫩草99av在线| 又污又爽又黄的网站| 亚洲人妻一区二区精品| 女人腿张开让男人桶爽| 亚洲成av人片在线观看www| 91精品乱码一区二区三区| 极品无码国模国产在线观看 | 国产美女裸身网站免费观看视频| 不卡乱辈伦在线看中文字幕| 皮山县| 91孕妇精品一区二区三区| 免费AV手机在线观看片| 中文字幕日韩国产精品| 天天做日日做天天添天天欢公交车| 色偷偷久久一区二区三区| 国产乱人偷精品人妻a片| 久久热99这里只有精品| 国产一区日韩二区三区| 亚洲精品成人片在线观看精品字幕 | 午夜福利看片在线观看| 亚洲中文久久久精品无码| 亚洲精品男男一区二区| 超碰成人人人做人人爽| 忘忧草www日本韩国| 国产永久免费高清在线| 国内自拍第一区二区三区| 欧产日产国产精品精品|