為什么我的裝飾器沒別人的強祝谚?

裝飾器(Decorator)是一種設(shè)計模式宪迟,允許向一個對象添加功能,但是又不改變其內(nèi)部結(jié)構(gòu)交惯。裝飾器只是ES7中提案次泽,目前處于Stage 2階段,但是不久的將來就會變成規(guī)范席爽。它主要用來修飾類以及類屬性意荤。

本文總共分為五個部分:

  • 修飾類
  • 修飾類屬性
  • 三個參數(shù)
  • 在React的應(yīng)用
  • Babel編譯

修飾類

@isPerson
class Person {}

function isPerson(target) {
  target.prototype.isPerson = true;
}

const person1 = new Person();
console.log(person1.isPerson); // true

上面有一個Person類,我們寫了一個isPerson裝飾器修飾它只锻,最終我們實例化Person時玖像,實例上多了isPerson屬性。

@isPerson(true)
class Person {}

function isPerson(bol) {
  return function(target) {
    target.prototype.isPerson = true;
  }
}

同樣我們也可以給裝飾器isPerson添加參數(shù)齐饮,而裝飾器內(nèi)容就會多一層結(jié)構(gòu)捐寥,return對一個對象笤昨。所以裝飾器是支持傳參和不傳參的。

本質(zhì)上可以把Person看作一個方法上真,而實際上裝飾器就是將方法當參數(shù)傳入咬腋。在Babel編譯我會講解裝飾器的本質(zhì)。

isPerson(true)(function Person() {})

修飾類屬性

class Person {
  firstName = 'Peter';
  lastName = 'Cheng';

  @readonly
  realName() { return `${this.firstName} ${this.lastName}` };
}

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

const person2 = new Person();
console.log(person2.realName = 1);

同時給類PersonrealName方法添加了readonly裝飾器睡互,當輸出實例的realName屬性時根竿,程序會報錯,Uncaught TypeError: Cannot assign to read only property 'realName' of object '#<Person>'就珠。注意寇壳,這里實際上修飾的是類方法,裝飾器目前不能修飾類里面的變量妻怎,比如firstNamelastName壳炎。

三個參數(shù)

  • target

如果修飾類,那target就是目標本身逼侦,第一個例子中就是Person類匿辩。如果你修飾的是類方法,那target就是類實例榛丢。

  • name

類名或者類方法名稱铲球,同樣第一個例子,打印出來就是Person晰赞。

  • descriptor

屬性的描述對象稼病。它具有如下幾個屬性,value掖鱼,enumerable然走,configurablewritable戏挡。value是修飾對象本身芍瑞,而其他值和Object.defineProperty的屬性一樣,控制值的行為褐墅。

 {
   value: ? realName(),
   enumerable: false,
   configurable: true,
   writable: false
 };

在React的應(yīng)用

裝飾器在React中的應(yīng)用我們隨處看見拆檬,比如Redux,自定義HOC等掌栅,其實這些都是高階函數(shù)的應(yīng)用秩仆。

下面我們實現(xiàn)一個打點裝飾器码泛,當我們觸發(fā)postClick方法時猾封,會輸出一個打點的log,最終會輸出postClick 2噪珊。我們通過攔截value晌缘,并重寫value齐莲,將參數(shù)id打印了出來。

class App extends Component {
  @analytic()
  postClick(id = 1) {}
}

function analytic(args) {
  return function decorator(target, name, descriptor) {
    if (typeof descriptor === 'undefined') {
      throw new Error('@analytic decorator can only be applied to class methods');
    }

    const value = descriptor.value;
    function newValue(...args) {
      console.log(`${name} ${args}`);
      return value.bind(this)(args);
    };

    
    return {
      ...descriptor,
      value: newValue
    };
  }
}

const app = new App();
app.postClick(2);

Babel編譯

我們選擇修飾類方法的例子磷箕,看一下最簡單的裝飾器如果編譯成ES5代碼會是怎么樣选酗。可以用Babel官方的網(wǎng)址 https://babeljs.io/repl岳枷,看一下第一個例子中代碼被編譯成什么樣子芒填。

class Person {
  firstName = 'Peter';
  lastName = 'Cheng';

  @readonly
  realName() { return `${this.firstName} ${this.lastName}` };
}

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

const person2 = new Person();
console.log(person2.realName = 1);

編譯之后

function _decorate(decorators, factory, superClass, mixins) {}

// 省略中間一大推

var Person = _decorate(null, function (_initialize) {
  var Person = function Person() {
    _classCallCheck(this, Person);

    _initialize(this);
  };

  return {
    F: Person,
    d: [{
      kind: "field",
      key: "firstName",
      value: function value() {
        return 'Peter';
      }
    }, {
      kind: "field",
      key: "lastName",
      value: function value() {
        return 'Cheng';
      }
    }, {
      kind: "method",
      decorators: [readonly],
      key: "realName",
      value: function realName() {
        return "".concat(this.firstName, " ").concat(this.lastName);
      }
    }]
  };
});

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

var person2 = new Person();
console.log(person2.realName = 1);

其實主要看Person對象有那些變化,Babel將類編譯成了ES5的function空繁,并且外面套一層裝飾器殿衰,但是裝飾器最終還是賦值給Person變量。內(nèi)部Person對象最終返回一個對象盛泡,而key為realName的對象有一個decorators闷祥,它是一個數(shù)組。我們看看decorators做了什么傲诵。其實就是遍歷數(shù)組凯砍,將參數(shù)最終映射到Object.defineProperty,操作對象的可寫入等屬性拴竹。

結(jié)束語

通過本文我們知道了裝飾器是什么悟衩,并且用來做什么,以及實質(zhì)是什么殖熟。最近看了一部電影《斯隆女士》局待,里面就有一個片段闡述專業(yè)性的重要性,作為前端菱属,首先需要掌握的就是ES相關(guān)的知識钳榨。終于整理完裝飾器的知識了,我最近正在用Node + Flutter做一個App纽门,最終計劃是發(fā)布上線薛耻,敬請期待。

我用create-react-app生成了一個項目赏陵,可以直接使用裝飾器饼齿。https://github.com/carrollcai/decorator-demo

參考

http://es6.ruanyifeng.com/#docs/decorator


寫作時間: 20190922

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蝙搔,一起剝皮案震驚了整個濱河市缕溉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌吃型,老刑警劉巖证鸥,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡枉层,警方通過查閱死者的電腦和手機泉褐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鸟蜡,“玉大人膜赃,你說我怎么就攤上這事∪嗤” “怎么了跳座?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長泣矛。 經(jīng)常有香客問我躺坟,道長,這世上最難降的妖魔是什么乳蓄? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任咪橙,我火速辦了婚禮,結(jié)果婚禮上虚倒,老公的妹妹穿的比我還像新娘美侦。我一直安慰自己,他們只是感情好魂奥,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布菠剩。 她就那樣靜靜地躺著,像睡著了一般耻煤。 火紅的嫁衣襯著肌膚如雪具壮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天哈蝇,我揣著相機與錄音棺妓,去河邊找鬼。 笑死炮赦,一個胖子當著我的面吹牛怜跑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播吠勘,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼性芬,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了剧防?” 一聲冷哼從身側(cè)響起植锉,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎峭拘,沒想到半個月后俊庇,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體搏熄,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年暇赤,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宵凌。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡鞋囊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瞎惫,到底是詐尸還是另有隱情溜腐,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布瓜喇,位于F島的核電站挺益,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏乘寒。R本人自食惡果不足惜望众,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望伞辛。 院中可真熱鬧烂翰,春花似錦、人聲如沸蚤氏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽竿滨。三九已至佳恬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間于游,已是汗流浹背毁葱。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贰剥,地道東北人头谜。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像鸠澈,于是被迫代替她去往敵國和親柱告。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345