JavaScript中的裝飾器--Decorator

什麼是Decorator

  修飾模式(Decortaor),是面向對象編程領域中,一種動態地往一個類中添加新的行爲的設計模式。就功能而言,修飾模式相比生成子類更爲靈活,這樣能夠給某個對象而不是整個類添加一些功能。html

Decorator的做用

  經過使用修飾模式,能夠在運行時擴充一個類的功能。原理是:增長一個修飾類包裹原來的類,包裹的方式通常是經過在將原來的對象做爲修飾類的構造函數的參數。裝飾類實現新的功能,可是,在不須要用到新功能的地方,它能夠直接調用原來的類中的方法。修飾類必須和原來的類有相同的接口。
  修飾模式是類繼承的另一種選擇。類繼承在編譯時候增長行爲,而裝飾模式是在運行時增長行爲。
  當有幾個相互獨立的功能須要擴充時,這個區別就變得很重要。在有些>面向對象的編程語言中,類不能在運行時被建立,一般在設計的時候也不能預測到有哪幾種功能組合。這就意味著要爲每一種組合建立一個新類。相反,修飾模式是面向運行時候的對象實例的,這樣就能夠在運行時根據須要進行組合。一個修飾模式的示例是JAVA裏的Java I/O Streams的實現。java

  上面兩段是維基百科中對於Decorator裝飾器模式的介紹.簡單來講.Decorator就是一種動態地往一個類中添加新的行爲的設計模式,它能夠在類運行時,擴展一個類的功能.而且去修改類自己的屬性和方法.使其能夠在不一樣類之間更靈活的共用一些屬性和方法.下面就讓咱們來看下在ES中Decorator的用法.node

Decorator的用法

類自己的修飾

  在ES中Decorator的具體表現形式爲:es6

一個求值結果爲函數的表達式,接受目標對象、名稱和裝飾器描述做爲參數,可選地返回一個裝飾器描述來安裝到目標對象上。編程

因此說在ES中Decorator的最終本質就是一個函數,這個函數經過接受目標對象的三個參數: 所裝飾的類的自己所裝飾的類的某個屬性的key值所裝飾的類的某個屬性的描述對象.並經過對這三個參數的操做,已達到爲類擴展功能的目的.下面然咱們來用具體代碼來演示一下:設計模式

@eat
class Person {
  constructor() {}
}

function eat(target, key, descriptor) {
  console.log('吃飯');
  console.log(target);
  console.log(key);
  console.log(descriptor);
  target.prototype.act = '我要吃飯';
}

const jack = new Person();
console.log(jack.act);

// 吃飯
// [Function: Person]
// undefined
// undefined
// 我要吃飯
複製代碼

  上面是一個最簡單的裝飾器的運用.咱們首先聲明一個類Person,而後在聲明一個裝飾器函數eat,在eat中將傳入的三個參數分別打印出來,並將第一個參數target的原型prototype上添加一個屬性act,並賦值爲'我要吃飯'.而後將函數eat做爲裝飾在Person這個類自己上.最後,構造一個Person的實例jack,並打印jack上的act屬性.瀏覽器

  而後從下面的運行結果中咱們能夠看出,代碼中會先打印出'吃飯',而後是參數target,其次是參數key,再而後是參數descriptor.最後纔是jackact屬性.這是由於裝飾器對類的行爲的改變,是代碼編譯時發生的,而不是在運行時。這意味着,裝飾器能在編譯階段運行代碼。也就是說,裝飾器本質就是編譯時執行的函數。babel

類的屬性的修飾

  看了上面那段代碼的運行結果,你可能會用這麼一個疑問.Decorator所傳進來的三個參數: targetkeydescriptor.爲何只有target有值,而keydescriptor則都是undefined了.事實上這是由於你將裝飾器Decorator裝飾在類自己上所致使的.在ES中裝飾器並不單單隻能裝飾在類自己上,也能夠裝飾在類的屬性上.當裝飾在類的屬性上時.keydescriptor也就有了用武之地.請看下面這段代碼:編程語言

class Person {
    constructor() {}

    @test
    name() {console.log('張三');}
}

function test(target, key, descriptor) {
  console.log(target);
  console.log(key);
  console.log(descriptor);
}

const student = new Person();

student.name();

// Person {}
// name
// { value: [Function: name], writable: true, enumerable: false, configurable: true }
// 張三
複製代碼

  在上面代碼中咱們將test裝飾器裝飾在Person類的name屬性上.而後打印三個傳入的入參.分別獲得了咱們指望的結果.而經過這三個參數,咱們就能夠對咱們要裝飾的對象進行一些有趣的修改, 以下面這樣:函數

class Person {

  constructor() {}
    @test
    name() {console.log('張三');}
}

function test(target, key, descriptor) {
  descriptor.value = function () {
    console.log('李四');
  }
}

const student = new Person();

student.name();

// 李四
複製代碼

  在上面代碼中,咱們給一個類Person的原型上賦值了一個屬性name,其值爲一個函數,執行時會打印張三兩個字.而後咱們給屬性name裝飾了test這個裝飾器.在test裝飾器中,將傳入進來的descriptor對象上的value賦值爲一個函數,執行時打印李四兩個字.最後構造一個實例student,並執行name方法,執行的結果是打印了李四兩個字,這說明經過裝飾器,咱們徹底能夠在不改變一個類自己的請況下對一個類的屬性進行改寫.這使得在不一樣類中共享同一方法這一操做至關簡單,且優雅.須要共享的使用使用裝飾器,不須要的時候移除裝飾器.徹底不用對類的自己進行操做.

  對於裝飾器,若是咱們感到,固定傳入的三個參數不夠用的話,咱們也能夠自行傳入參數只須要像下面這麼寫:

function rename(name) {
  return function(target, key, descriptor) {
    descriptor.value = name;
  }
}

複製代碼

前面已經說過,在ES中**Decorator就是一個求值結果爲函數的表達式,**因此,只要你最後的返回結果是一個函數.都是一個合法的裝飾器.

Decorator的兼容性

  目前ES中Decorator還處於提案階段,各大瀏覽器和node,均未公開支持這一特性.若是想要使用,則須要借用babel的一個插件babel-plugin-transform-decorators才能夠.

結束

  上面是我對Decorator的一些看法,但願對你們有所幫助.若是文中任有何不當之處請予以斧正,謝謝

參考資料:

個人我的網址: wangyiming.info

相關文章
相關標籤/搜索