Angular專(zhuān)題】 (3)裝飾器decorator,一塊語(yǔ)法糖

      網(wǎng)友投稿 1073 2025-04-01

      【Angular專(zhuān)題】 (3)裝飾器decorator,一塊語(yǔ)法糖

      一. Decorator裝飾器二. Typescript中的裝飾器2.1 類(lèi)裝飾器2.2 方法裝飾器2.3 訪(fǎng)問(wèn)器裝飾器2.4 屬性裝飾器2.5 參數(shù)裝飾器三. 用ES5代碼模擬裝飾器功能四. 小結(jié)

      一. Decorator裝飾器

      修飾器是ES7加入的新特性,Angular中進(jìn)行了大量使用,有很多內(nèi)置的修飾器,后端的同學(xué)一般稱(chēng)之為“注解”。修飾器的作用,實(shí)際上就是設(shè)計(jì)模式中常說(shuō)的裝飾者模式的一種實(shí)現(xiàn),早在ES6開(kāi)始,設(shè)計(jì)模式原生化就已經(jīng)是非常明顯的趨勢(shì)了,無(wú)論是for..of..和Iterator接口的配合內(nèi)化了迭代者模式,Proxy對(duì)象實(shí)現(xiàn)的代理模式等等,都可以看出Javascript逐漸走向標(biāo)準(zhǔn)化的趨勢(shì)和決心。

      裝飾者模式,是指在不必改變?cè)?lèi)文件或使用繼承的情況下,動(dòng)態(tài)地?cái)U(kuò)展一個(gè)對(duì)象的功能,為對(duì)象增加額外特性的一種設(shè)計(jì)模式。考慮到j(luò)avascript中函數(shù)參數(shù)為對(duì)象時(shí)只傳遞地址這一特性,裝飾者模式實(shí)際上是非常好復(fù)現(xiàn)的,掌握其基本知識(shí)對(duì)于理解Angular技術(shù)棧的原理和執(zhí)行流程是必不可少的,從結(jié)果的角度來(lái)看,使用裝飾器和直接修改類(lèi)的定義沒(méi)有什么區(qū)別,但使用裝飾器更符合開(kāi)放封閉原則,且更符合聲明式的思想,本文著重分析Typescript中支持的幾種不同的裝飾器用法。

      二. Typescript中的裝飾器

      2.1 類(lèi)裝飾器

      類(lèi)裝飾器,就是用來(lái)裝飾類(lèi)的,它只接受一個(gè)參數(shù),就是被裝飾的類(lèi)。下面的示例使用@testable修飾器為已定義的類(lèi)加上一個(gè)__testable屬性:

      //裝飾器修改的是類(lèi)定義的表現(xiàn),故在javascript中模擬時(shí)需要直接將變化添加至原型上

      function testable(target: Function):void{

      target.prototype.__testable = false;

      }

      //使用類(lèi)裝飾器

      @testable

      class Person{

      constructor(){}

      }

      //測(cè)試裝飾后的結(jié)果

      let person = new Person();

      console.log(person.__testable);//false

      另一方面,我們可以使用工廠(chǎng)函數(shù)的方法生成一個(gè)可接收附加參數(shù)的裝飾器,借助高階函數(shù)的思路不難理解,例如Angular中常見(jiàn)的這種形式:

      //Angular中的組件定義

      @Component({

      selector:'hero-detail',

      templateUrl:'hero-detail.html',

      styleUrls:['style.css']

      })

      export Class MyComponent{

      constructor(){}

      }

      //@Component裝飾者類(lèi)的作用機(jī)制可以理解為:

      function Component(params:any){

      return function(target: Function):void{

      target.prototype.metadata = params;

      }

      }

      這樣在組件被實(shí)例化時(shí),就可以獲取到傳入的元數(shù)據(jù)信息。換句話(huà)說(shuō),Component({...})執(zhí)行后返回的函數(shù)才是真正的類(lèi)裝飾器,Component是一個(gè)接受參數(shù)然后生成裝飾器的函數(shù),也就是裝飾器工廠(chǎng),從元編程的角度來(lái)講,相當(dāng)于修改了new操作符的行為。

      2.2 方法裝飾器

      方法修飾器聲明在一個(gè)方法的聲明之前,會(huì)被應(yīng)用到方法的屬性描述符上,可以用來(lái)檢視,修改或者替換方法定義。它接收如下三個(gè)參數(shù):

      1.靜態(tài)成員時(shí)參數(shù)是類(lèi)的構(gòu)造函數(shù),實(shí)例成員時(shí)傳入類(lèi)的原型對(duì)象。

      2.成員名

      3.成員屬性描述符

      下面的裝飾器@enumerable將被修飾對(duì)象修改為可枚舉:

      //方法裝飾器,返回值會(huì)直接賦值給方法的屬性描述符。

      function enumerable(target: any, propertyKey: string, descriptor:PropertyDescriptor):void{

      descriptor.enumerable = true;

      }

      class Person{

      constructor(){}

      @enumerable//使用方法裝飾器

      sayHi(){

      console.log('Hi');

      }

      }

      //測(cè)試裝飾后的結(jié)果

      let person = new Person();

      console.log(person.__testable);//false

      更常用的方式依然是利用高階函數(shù)返回一個(gè)可被外部控制的裝飾器:

      function enumerable(value: boolean){

      return function (target: any, propertyKey: string, descriptor:PropertyDescriptor):void{

      descriptor.enumerable = true;

      }

      }

      2.3 訪(fǎng)問(wèn)器裝飾器

      訪(fǎng)問(wèn)器,一般指屬性的get/set方法,和普通方法裝飾器用法一致,需要注意的是typescript中不支持同時(shí)裝飾一個(gè)成員的get訪(fǎng)問(wèn)器和set訪(fǎng)問(wèn)器。

      2.4 屬性裝飾器

      屬性裝飾器表達(dá)式運(yùn)行時(shí)接收兩個(gè)參數(shù):

      1.對(duì)于靜態(tài)成員來(lái)說(shuō)是類(lèi)的構(gòu)造函數(shù),對(duì)于實(shí)例成員來(lái)說(shuō)是類(lèi)的原型對(duì)象。

      【Angular專(zhuān)題】 (3)裝飾器decorator,一塊語(yǔ)法糖

      2.成員名

      Typescript官方文檔給出的示例是這樣的:

      class Greeter {

      @format("Hello, %s") greeting: string;

      constructor(message: string){

      this.greeting = message;

      }

      greet(){

      let formatString = getFormat(this, 'greeting');

      return formatString.replace('s%',this.greeting);

      }

      }

      然后定義@format裝飾器和getFormat函數(shù):

      .import "reflect-metadata";

      const formatMetadataKey = Symbol("format");

      function format(formatString: string) {

      return Reflect.metadata(formatMetadataKey, formatString);

      }

      function getFormat(target: any, propertyKey: string) {

      return Reflect.getMetadata(formatMetadataKey, target, propertyKey);

      }

      與方法裝飾器相比,屬性裝飾器的形參列表中并沒(méi)有屬性描述符,因?yàn)槟壳皼](méi)有辦法在定義一個(gè)原型對(duì)象的成員時(shí)描述一個(gè)實(shí)例屬性,也無(wú)法監(jiān)視屬性的初始化方法。TS中的屬性描述符單獨(dú)使用時(shí)只能用來(lái)監(jiān)視類(lèi)中是否聲明了某個(gè)名字的屬性,示例中通過(guò)外部功能擴(kuò)展了其實(shí)用性。Angular中最常見(jiàn)的屬性修飾器就是Input( )和output( )。

      2.5 參數(shù)裝飾器

      參數(shù)裝飾器一般用于裝飾參數(shù),在類(lèi)構(gòu)造函數(shù)或方法聲明中裝飾形參。

      它在運(yùn)行時(shí)被當(dāng)做函數(shù)調(diào)用,傳入下列3個(gè)參數(shù):

      1.靜態(tài)成員時(shí)接收構(gòu)造函數(shù),實(shí)例成員時(shí)接收原型對(duì)象。

      2.成員名

      3.參數(shù)在函數(shù)參數(shù)列表中的索引。

      TS中參數(shù)裝飾器單獨(dú)使用時(shí)只能用來(lái)監(jiān)視一個(gè)方法的參數(shù)是否被傳入,Typescript官方給出的示例如下:

      class Greeter {

      greeting: string;

      constructor(message: string) {

      this.greeting = message;

      }

      @validate

      greet(@required name: string) {//此處使用了參數(shù)修飾符

      return "Hello " + name + ", " + this.greeting;

      }

      }

      兩個(gè)裝飾器的定義如下:

      import "reflect-metadata";

      const requiredMetadataKey = Symbol('required');

      /*

      *@required參數(shù)裝飾器

      *實(shí)現(xiàn)的功能就是當(dāng)函數(shù)的參數(shù)必須填入時(shí),將相關(guān)信息存儲(chǔ)到一個(gè)外部的數(shù)組中,可以看出參數(shù)裝飾器并*未對(duì)參數(shù)本身做出什么修改。

      */

      function required(target: Object, propertyKey:string | symbol, parameterIndex: number){

      let existingRequiredParameters: number[] = Reflect.getOwnMetadata(requiredMetadataKey, target, propertyKey) || [];

      existingRequiredParameters.push(parameterIndex);

      Reflect.defineMetadata(requiredMetadataKey, existingRequiredParameters, target, propertyKey);

      }

      /*

      *@validate裝飾器為方法裝飾器

      *展示了如何通過(guò)操作方法屬性描述符中的value屬性來(lái)實(shí)現(xiàn)方法的代理訪(fǎng)問(wèn)。

      */

      function validate(target:any, propertyName: string, descriptor:TypedPropertyDescriptor){

      let method = descriptor.value;//方法的屬性修飾符的value就是方法的函數(shù)表達(dá)式

      descriptor.value = function(){

      let requiredParameters: number[] = Reflect.getOwnMetadata(requiredMetadataKey, target, propertyName);//在外部存儲(chǔ)中查找是否有必填參數(shù)

      if (requiredParameters){

      for(let parameterIndex of requiredParameters){

      if(parameterIndex >= arguments.length || arguments[parameterIndex] === undefined){

      //傳入?yún)?shù)不足或被約束參數(shù)為undefined時(shí)拋出錯(cuò)誤。

      throw new Error('Missing required argument');

      }

      }

      }

      return method.apply(this, arguments);//如果沒(méi)有任何錯(cuò)誤拋出則繼續(xù)執(zhí)行原函數(shù)

      }

      }

      在Typescript中,裝飾器的運(yùn)行順序基本依照參數(shù)裝飾器,方法裝飾器,訪(fǎng)問(wèn)符裝飾器,屬性裝飾器,類(lèi)裝飾器這樣的順序來(lái)運(yùn)行,所以參數(shù)裝飾器和方法裝飾器可以聯(lián)合使用實(shí)現(xiàn)一些額外功能。

      三. 用ES5代碼模擬裝飾器功能

      用ES5來(lái)模擬一下上述的方法裝飾器和參數(shù)裝飾器聯(lián)合作用的例子,就很容易看出裝飾器的作用:

      //使用ES5語(yǔ)法模擬裝飾器

      function Greeter(message){

      this.greeting = message;

      }

      Greeter.prototype.greet = function(name){

      return "Hello " + name + ", " + this.greeting;

      }

      //外部存儲(chǔ)的必要性校驗(yàn)

      requiredArray = {};

      //參數(shù)裝飾器

      function requireDecorator(FnKey,paramsIndex){

      requiredArray[FnKey] = paramsIndex;

      }

      //裝飾器函數(shù)

      function validateDecorator(Fn,FnKey){

      let method = Fn;

      return function(){

      let checkParamIndex = requiredArray[FnKey];

      if(checkParamIndex > arguments.length-1 || arguments[checkParamIndex] === undefined){

      throw new Error('params invalid');

      }

      return method.apply(this, arguments);

      }

      }

      //運(yùn)行裝飾

      requireDecorator('greet',0);

      Greeter.prototype.greet = validateDecorator(Greeter.prototype.greet, 'greet');

      //測(cè)試裝飾

      let greeter = new Greeter('welcome to join the conference');

      console.log(greeter.greet('Tony'));

      console.log(greeter.greet());

      在node環(huán)境中運(yùn)行一下就可以看到,greet( )方法在未傳入?yún)?shù)時(shí)會(huì)報(bào)錯(cuò)提示。

      四. 小結(jié)

      裝飾器實(shí)際上就是一種更加簡(jiǎn)潔的代碼書(shū)寫(xiě)方式,從代碼表現(xiàn)來(lái)理解,就是使用閉包和高階函數(shù)擴(kuò)展或者修改了原來(lái)的表現(xiàn),從功能角度來(lái)理解,達(dá)到了不修改內(nèi)部實(shí)現(xiàn)的前提下動(dòng)態(tài)擴(kuò)展和修改類(lèi)定義的目的。

      Elasticsearch Angular

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶(hù)投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶(hù)投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:excel中VBA循環(huán)嵌套怎么填充表格顏色?
      下一篇:MySQL 配置InnoDB配置非持久優(yōu)化器統(tǒng)計(jì)信息參數(shù)
      相關(guān)文章
      亚洲一区二区三区无码国产| 亚洲综合激情另类小说区| 亚洲婷婷天堂在线综合| 亚洲精品国产精品乱码不卡√ | 国产亚洲精品2021自在线| 亚洲欧美精品午睡沙发| 亚洲综合av一区二区三区不卡 | 亚洲六月丁香婷婷综合| 亚洲三级在线播放| 精品亚洲成在人线AV无码| 国产亚洲国产bv网站在线| 亚洲高清一区二区三区| 亚洲国产系列一区二区三区 | 精品久久久久久亚洲| 亚洲s色大片在线观看| 亚洲AV无码乱码国产麻豆穿越 | 久久香蕉国产线看观看亚洲片| 亚洲成AV人片在线播放无码| 狠狠色伊人亚洲综合成人| 亚洲va在线va天堂va888www| 中文字幕亚洲色图| 亚洲另类春色校园小说| 亚洲一区精彩视频| 亚洲国产精品无码久久久秋霞1| 激情无码亚洲一区二区三区| 亚洲国产成人久久综合一区77| 久久精品国产亚洲一区二区三区| 国产亚洲精久久久久久无码77777| 亚洲桃色AV无码| 亚洲国产天堂在线观看| 亚洲第一区视频在线观看| 亚洲久悠悠色悠在线播放| 亚洲日韩精品无码AV海量| 亚洲成a人片在线不卡一二三区| 怡红院亚洲红怡院在线观看| 久久精品夜色噜噜亚洲A∨| 亚洲爱情岛论坛永久| 亚洲不卡1卡2卡三卡2021麻豆| 亚洲熟女精品中文字幕| 午夜亚洲福利在线老司机| 伊伊人成亚洲综合人网7777|