<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Decorator 裝飾器

          共 7626字,需瀏覽 16分鐘

           ·

          2022-04-11 22:14

          大廠技術(shù)??高級前端??Node進階

          點擊上方?程序員成長指北,關(guān)注公眾號

          回復(fù)1,加入高級Node交流群


          前言

          大家在前端開發(fā)過程中有遇到過 @ + 方法名 這種寫法嗎?當我第一次看到的時候,直接懵了,這是什么東東……

          遇到困難解決困難,在我的一番查找后,我知道了,原來這東西叫裝飾器,英文名叫 Decorator ,那它到底是干什么的呢?接下來就讓我跟大家說道說道~

          什么是裝飾器

          裝飾者模式

          裝飾者模式就是能夠在不改變對象自身的基礎(chǔ)上,在程序運行期間給對象動態(tài)地添加職責(zé)。打個比方,一個人在天氣冷的時候要穿棉衣,天氣熱的時候穿短袖,可無論穿什么,本質(zhì)上他還是一個人,只不過身上穿了不同的衣服。

          所以簡單來說, Decorator 就是一種動態(tài)地往一個類中添加新的行為的設(shè)計模式, 它可以在類運行時, 擴展一個類的功能, 并且去修改類本身的屬性和方法, 使其可以在不同類之間更靈活的共用一些屬性和方法。

          @ 是針對這種設(shè)計模式的一個語法糖,不過目前還處于第 2 階段提案中,使用它之前需要使用 Babel 模塊編譯成 ES5 或 ES6。

          怎么使用裝飾器

          三方庫使用

          Babel 版本 ≥ 7.x

          如果項目的 Babel 版本大于等于 7.x,那么可以使用 @babel/plugin-proposal-decorators

          • 安裝

            npm?install?--save-dev?@babel/plugin-proposal-decorators
          • 配置 .babelrc

            {
            ??"plugins":?[
            ????["@babel/plugin-proposal-decorators",?{?"legacy":?true?}],
            ??]
            }

          Babel 版本 ≤ 6.x

          如果小于等于 6.x,則可以使用 babel-plugin-transform-decorators-legacy

          • 安裝

            npm?install?--save-dev?@babel/plugin-proposal-decorators
          • 配置 .babelrc

            {
            ????"plugins":?["transform-decorators-legacy"]
            }

          使用方法

          裝飾器的寫法是 @ + 返回裝飾器函數(shù)的表達式,所以其使用方法如下:

          @classDecorator
          class?TargetClass?{?//?類
          ??@fieldDecorator
          ??targetField?=?0;?//?類實例屬性

          ??@funDecorator
          ??targetFun()?{?}?//?類方法

          ??@accessorDecorator
          ??get?targetGetFun()?{?}?//?類訪問器
          }

          如果一個對象使用多個裝飾器,那么執(zhí)行順序是什么呢?

          function?decorator1()?{
          ??console.log('decorator1');
          ??return?function?decFn1(targetClass)?{
          ????console.log('decFn1');
          ????return?targetClass;
          ??};
          }

          function?decorator2()?{
          ??console.log('decorator2');
          ??return?function?decFn2(targetClass)?{
          ????console.log('decFn2');
          ????return?targetClass;
          ??};
          }

          執(zhí)行順序:

          打印結(jié)果:

          根據(jù)以上,我們可知,裝飾器的執(zhí)行順序為由外向內(nèi)進入,由內(nèi)向外執(zhí)行。

          使用范圍

          根據(jù)使用方法,我們可以看出裝飾器可以應(yīng)用于以下幾種類型:

          • 類(class)
          • 類實例屬性(公共、私有和靜態(tài))
          • 類方法(公共、私有和靜態(tài))
          • 類訪問器(公共、私有和靜態(tài))

          函數(shù)的裝飾

          當我們看完裝飾器的使用方法和使用范圍時,我們發(fā)現(xiàn),裝飾器不能修飾函數(shù),那原因到底是什么呢?原因就是函數(shù)有函數(shù)提升

          var?num?=?0;
          function?add?()?{
          ??num?++;
          }
          @add
          function?fn()?{}

          在這個例子中,我們想要在執(zhí)行后讓 num 等于 1,但其實結(jié)果并不是這樣,因為函數(shù)提升,實際上代碼是這樣執(zhí)行的:

          function?add?()?{
          ??num?++;
          }
          @add
          function?fn()?{}
          var?num;
          num?=?0;

          如果一定要裝飾函數(shù)的話,可以采用高階函數(shù)的形式,這篇文章主要講裝飾器,有關(guān)高階函數(shù)就不在此贅述了,不了解的小伙伴們可自行查閱資料哈~

          裝飾器原理

          根據(jù)裝飾器的使用范圍,可以把它分為兩大類:類的裝飾與類方法的裝飾,下面就讓我為大家逐個分享一下。

          類的裝飾

          傳參

          首先我們先根據(jù)一個小例子看一下裝飾器接收參數(shù)的情況:

          function?decorator(...args)?{
          ??args.forEach((arg,?index)?=>?{
          ????console.log(`參數(shù)${index}`,?arg);
          ??});
          }

          @decorator
          class?TargetClass?{?}

          console.log('targetClass:',?TargetClass);

          打印結(jié)果如下:

          看到結(jié)果,我們發(fā)現(xiàn)裝飾器只接收一個參數(shù),就是被裝飾的類定義本身。

          返回值

          我們繼續(xù)通過一個小例子來看返回值的情況:

          function?returnStr(targetClass)?{
          ??return?'hello?world~';
          }

          function?returnClass(targetClass)?{
          ??return?targetClass;
          }

          @returnStr
          class?ClassA?{?}

          @returnClass
          class?ClassB?{?}

          console.log('ClassA:',?ClassA);
          console.log('ClassB:',?ClassB);

          結(jié)果如下:

          根據(jù)結(jié)果,我們發(fā)現(xiàn)裝飾器返回什么輸出的就是什么。

          結(jié)論

          通過以上的兩個例子,我們可以得出以下這個結(jié)論:

          @decorator
          class?TargetClass?{?}

          //?等同于

          class?TargetClass?{?}
          TargetClass?=?decorator(TargetClass)?||?TargetClass;

          所以說,裝飾器的第一個參數(shù)就是要裝飾的類,它的功能就是對類進行處理。

          類裝飾器的使用

          • 添加屬性

            因為裝飾器接收的參數(shù)就是類定義本身,所以我們可以給類添加屬性:

            function?addAttribute(targetClass)?{
            ??targetClass.isUseDecorator?=?true;
            }

            @addAttribute
            class?TargetClass?{?}

            console.log(TargetClass.isUseDecorator);?//?true

            在這個例子中,我們定義了 addAttribute 的裝飾器,用于對 TargetClass 添加 isUseDecorator 標記,這個用法就跟 Java 中的注解比較相似,僅僅是對目標類型打上一些標記。

          • 返回裝飾器函數(shù)的表達式

            上面有說裝飾器的寫法是 @ + 返回裝飾器函數(shù)的表達式,也就是說,@ 后邊可以不是一個方法名,還可以是能返回裝飾器函數(shù)的表達式

            function?addAttribute(content)?{
            ??return?function?decFn(targetClass)?{
            ????targetClass.content?=?content;
            ????return?targetClass;
            ??};
            }

            @addAttribute('這是內(nèi)容~~~')
            class?TargetClass?{?}

            console.log(TargetClass.content);?//?這是內(nèi)容~~~

            我們看到 TargetClass 通過 addAttribute 的裝飾,添加了 content 這個屬性,并且可以向 addAttribute 傳參來給 content 屬性賦值,這種使用方法使裝飾器變得更加靈活。

          • 添加原型方法

            在前面的例子中我們添加的都是類的靜態(tài)屬性,但是既然裝飾器接收的參數(shù)就是類定義本身,那么它也可以通過訪問類的 prototype 屬性來添加或修改原型方法:

            function?decorator(targetClass)?{
            ??targetClass.prototype.decFun?=?function?()?{
            ????console.log('這里是裝飾器?decorator?添加的原型方法?decFun~');
            ??};
            }

            @decorator
            class?TargetClass?{?}

            const?targetClass?=?new?TargetClass();

            console.log(targetClass);
            targetClass.decFun();

            結(jié)果如下:

          以上就是類裝飾器的使用,由此我們可以得出,裝飾器還可以對類型進行靜態(tài)標記和方法擴展,還挺有用的對吧~那么看到這里,小伙伴們是不是發(fā)現(xiàn)了在實際項目中就有類裝飾器的使用,比如 react-redux 的 connect 就是一個類裝飾器、Antd 中的 Form.create 也是一個類裝飾器。

          //?connect
          class?App?extends?React.Component?{}
          export?default?connect(mapStateToProps,?mapDispatchToProps)(App);

          //?等同于

          @connect(mapStateToProps,?mapDispatchToProps)
          export?default?class?App?extends?React.Component?{}

          //?Form.create
          const?WrappedApp?=?Form.create()(App);

          //?等同于

          @Form.create()
          class?App?extends?React.Component?{}

          類方法的裝飾

          傳參

          我們把類實例屬性、類方法、類訪問器都歸到這一類中的原因其實是因為它們?nèi)齻€就是作為某個對象的屬性(實例屬性、原型方法、實例訪問器屬性),也就是說它們接收的參數(shù)是類似的:

          function?decorator(...args)?{
          ??args.forEach((arg,?index)?=>?{
          ????console.log(`參數(shù)${index}`,?arg);
          ??});
          ??console.log('****************');
          }

          class?TargetClass?{
          ??@decorator
          ??field?=?0;

          ??@decorator
          ??fn()?{?}

          ??@decorator
          ??get?getFn()?{?}
          }

          const?targetOne?=?new?TargetClass();
          console.log(targetOne.field,?Object.getOwnPropertyDescriptor(targetOne,?'field'));

          結(jié)果如下:

          根據(jù)結(jié)果我們發(fā)現(xiàn),類方法裝飾器接收了三個參數(shù):類定義對象、實例屬性/方法/實例訪問器屬性名、屬性操作符。眼熟吧,沒錯,它與 Object.defineProperty() 接收的參數(shù)很像。

          Object.defineProperty(obj, props, descriptor)

          Object.defineProperty() 的作用就是直接在一個對象上定義一個新屬性,或者修改一個對象的現(xiàn)有屬性,并返回此對象。該方法一共接收三個參數(shù):

          • 要定義屬性的對象(obj)
          • 要定義或修改的屬性名或 Symbol (props)
          • 要定義或修改的屬性描述符(descriptor)

          而對象里目前存在的屬性描述符有兩種主要形式:數(shù)據(jù)描述符存取描述符數(shù)據(jù)描述符是一個具有值的屬性,該值可以是可寫的,也可以是不可寫的;存取描述符是由 getter 函數(shù)和 setter 函數(shù)所描述的屬性。一個描述符只能是這兩者其中之一,不能同時是兩者。

          它們共享以下可選鍵值:

          • configurable

            屬性是否可以被刪除和重新定義特性,默認值為 false

          • enumerable

            是否會出現(xiàn)在對象的枚舉屬性中,默認值為 false

          數(shù)據(jù)描述符特有鍵值:

          • value

            該屬性對應(yīng)的值,默認值為 undefined

          • writable

            是否可以被更改,默認值為 false

          存取操作符特有鍵值:

          • get

            屬性的 getter 函數(shù),如果沒有 getter,則為 undefined;默認為?undefined

          • set

            屬性的 setter 函數(shù),如果沒有 setter,則為 undefined;默認為?undefined

          講完 Object.defineProperty(),接下來就讓我們看看該怎么使用它吧~

          類方法裝飾器的使用

          讓我們通過一個例子來了解一下:

          function?readonly(target,?name,?descriptor)?{
          ??descriptor.writable?=?false;
          ??return?descriptor;
          }

          class?Person?{
          ??@readonly
          ??name?=?'zhangsan';
          }

          const?person?=?new?Person();
          console.log(person.name,?Object.getOwnPropertyDescriptor(person,?'name'));

          打印結(jié)果如下:

          上面代碼說明,裝飾器會修改屬性的描述對象,然后被修改的描述對象再用來定義屬性。

          結(jié)論

          由此我們可以得出結(jié)論:

          function?changeName(target,?name,?descriptor)?{
          ??descriptor.value?=?'lisi';
          ??return?descriptor;
          }
          class?Person?{
          ??@changeName
          ??name?=?'zhangsan';
          }
          const?person?=?new?Person();

          //?等同于

          class?Person?{
          ??name?=?'zhangsan';
          }
          const?person?=?new?Person();
          Object.defineProperty(person,?'name',?{
          ??value:?'lisi',
          });

          裝飾器的應(yīng)用

          在項目中,可能會遇到這樣一種情況,好幾個組件的數(shù)據(jù)都是調(diào)用同一個后端接口獲得,只是傳參不同,有些小伙伴們在寫代碼的時候可能就是每個組件都去手動調(diào)用一次后端接口(以 React 項目為例):

          ...
          export?default?class?CompOne?extends?Component?{
          ??...
          ??getData?=?async?()?=>?{?//?調(diào)用后端接口
          ????const?data?=?await?request('/xxx',?{
          ??????params:?{
          ????????id:?'123',?//?不同組件傳參不同
          ??????},
          ????});
          ????this.setState({?data?});
          ??}
          ??render()?{
          ????...
          ????return?(
          ??????<div>
          ????????...
          ????????我是組件一:?{data}
          ????????...
          ??????div>

          ????)
          ??}
          }

          遇到這種情況,我們就可以用裝飾器解決呀~

          //?裝飾器
          function?getData(params)?{
          ??return?(Comp)?=>?{
          ????class?WrapperComponent?extends?Component?{
          ??????...
          ??????getData?=?async?()?=>?{
          ????????const?data?=?await?request('/xxx',?{
          ??????????params,
          ????????});
          ????????this.setState({?data?});
          ??????}
          ??????render()?{
          ????????...
          ????????return?(
          ??????????<Comp?data={data}?/>
          ????????)
          ??????}
          ????}

          ????return?WrapperComponent;
          ??}
          }

          //?組件
          ...
          @getData({
          ??id:?'123'
          })
          export?default?class?index?extends?Component?{
          ??...
          ??render()?{
          ????...
          ????const?data?=?this.props.data;?//?直接從?this.props?中獲取想要的數(shù)據(jù)
          ????return?(
          ??????<div>
          ????????...
          ????????我是組件一:?{data}
          ????????...
          ??????div>

          ????)
          ??}
          }

          總結(jié)

          好啦,今天的分享就要到此結(jié)束了哦,希望通過這篇文章大家能夠?qū)ρb飾器有一定的了解,如有不同意見,歡迎在評論區(qū)評論呦~就讓暴風(fēng)雨來得更猛烈些吧!

          參考鏈接

          裝飾器(https://www.bookstack.cn/read/es6-3rd/docs-decorator.md)

          ES7 提案: Decorators 裝飾器(https://blog.csdn.net/weixin_44691608/article/details/117180409)

          Object.defineProperty()(https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty)

          babel-plugin-transform-decorators-legacy(https://www.npmjs.com/package/babel-plugin-transform-decorators-legacy)

          Node 社群



          我組建了一個氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對Node.js學(xué)習(xí)感興趣的話(后續(xù)有計劃也可以),我們可以一起進行Node.js相關(guān)的交流、學(xué)習(xí)、共建。下方加 考拉 好友回復(fù)「Node」即可。



          如果你覺得這篇內(nèi)容對你有幫助,我想請你幫我2個小忙:

          1. 點個「在看」,讓更多人也能看到這篇文章
          2. 訂閱官方博客?www.inode.club?讓我們一起成長

          點贊和在看就是最大的支持??

          瀏覽 92
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本人操逼视频 | 亚洲一级Av无码毛片久久精品 | 天天弄天天干 | 少妇做爱视频 | 日韩污视频 |