成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

【用故事解讀 MobX 源碼(四)】裝飾器 和 Enhancer

maybe_009 / 1347人閱讀

摘要:所以這是一篇插隊(duì)的文章,用于去理解中的裝飾器和概念。因此,該的作用就是根據(jù)入?yún)⒎祷鼐唧w的描述符。其次局部來看,裝飾器具體應(yīng)用表達(dá)式是,其函數(shù)簽名和是一模一樣。等裝飾器語法,是和直接使用是等效等價(jià)的。

================前言===================

初衷:以系列故事的方式展現(xiàn) MobX 源碼邏輯,盡可能以易懂的方式講解源碼;

本系列文章

《【用故事解讀 MobX源碼(一)】 autorun》

《【用故事解讀 MobX源碼(二)】 computed》

《【用故事解讀 MobX源碼(三)】 shouldCompute》

《【用故事解讀 MobX 源碼(四)】裝飾器 和 Enhancer》

《【用故事解讀 MobX 源碼(五)】 Observable》

文章編排:每篇文章分成兩大段,第一大段以簡單的偵探系列故事的形式講解(所涉及人物、場景都以 MobX 中的概念為原型創(chuàng)建),第二大段則是相對(duì)于的源碼講解。

本文基于 MobX 4 源碼講解

=======================================

按照步驟,這篇文章應(yīng)該寫 觀察值(Observable)的,不過在撰寫的過程中發(fā)現(xiàn),如果不先搞明白裝飾器和 Enhancer(對(duì)這個(gè)單詞陌生的,先不要著急,繼續(xù)往下看) ,直接去解釋觀察值(Observable)會(huì)很費(fèi)勁。因?yàn)樵?MobX 中是使用裝飾器設(shè)計(jì)模式實(shí)現(xiàn)觀察值的,所以說要先掌握裝飾器,才能進(jìn)一步去理解觀察值。

所以這是一篇 “插隊(duì)” 的文章,用于去理解 MobX 中的裝飾器和 Enhancer 概念。

A. 本文目標(biāo)

本文主要解決我個(gè)人在源碼閱讀中的疑惑:

在官方文檔 如何(不)使用裝飾器 中,為什么說開啟 @observable、@computer 等裝飾器語法,是和直接使用 decorate 是等效的?

在 MobX 源碼中時(shí)常出現(xiàn)的 Enhancer 到底是個(gè)什么概念?它在 MobX 體系中發(fā)揮怎樣的作用?它和裝飾器又是怎么樣的一層關(guān)系?

如果你也有這樣的疑惑,不妨繼續(xù)閱讀本文,歡迎一起討論。

至于 觀察值(Observable),在本文中你只要掌握住 官方文檔 observable 的用法就足夠了,比如(示例摘自官方文檔):

const person = observable({
    firstName: "Clive Staples",
    lastName: "Lewis"
});
person.firstName = "C.S.";

const temperature = observable.box(20);
temperature.set(25);

對(duì)于 observable 方法的源碼解析將在下一篇中詳細(xì)展開,此篇文章不會(huì)做過多的討論。

B. 學(xué)會(huì)裝飾器 1、裝飾器基礎(chǔ)知識(shí)

和其他語言(Python、Java)一樣,裝飾器語法是借助 @ 符號(hào)實(shí)現(xiàn)的,現(xiàn)在問題就歸結(jié)到如何用 JS 去實(shí)現(xiàn) @ 語法。

對(duì)于還不熟悉裝飾器語法的讀者,這里推薦文章 《ES7 Decorator 裝飾者模式》,以鋼鐵俠為例,通過裝備特殊的裝備就能將普通人變成鋼鐵俠,簡單概括起來就是:

裝飾器設(shè)計(jì)模式的理念就和上面那樣的樸素,在不改造 托尼·史塔克(Tony Stark) 本體的前提下,通過加裝 盔甲飛行器 的方式增強(qiáng) Tony 的能力,從而“變成”鋼鐵俠。

有關(guān)裝飾器使用的文章,還可以參考這兩篇參考文章 探尋 ECMAScript 中的裝飾器 Decorator、細(xì)說ES7 JavaScript Decorators

文章都比較早,當(dāng)時(shí)寫文章的作者都認(rèn)為在新的 ES7 里會(huì)推出標(biāo)準(zhǔn)的 @ 語法,然而事后證明官方并沒有這個(gè)意愿。我們知道目前的 ECMAScript 2015 標(biāo)準(zhǔn),甚至到 ECMAScript 2018 標(biāo)準(zhǔn)官方都沒有提供 @ 語法的支持,我們在其他文章中看到的 @ 語法都是通過 babel 插件來實(shí)現(xiàn)的。

上面提及的參考文章都是屬于應(yīng)用類型的,就是直接使用裝飾器語法(即直接使用 @ 語法)來展示裝飾器的實(shí)際應(yīng)用,而對(duì)于如何實(shí)現(xiàn) @ 語法并沒有提及 —— 那就是如何用 Object.defineProperty 來實(shí)現(xiàn) @ 語法。

道理大家都懂,那么到底如何才能自己動(dòng)手去實(shí)現(xiàn) @ 裝飾器語法呢?

2、首先你要理解屬性描述符(descriptor)

在 JS 中,我們借助 Object.defineProperty 方法實(shí)現(xiàn)裝飾器設(shè)計(jì)模式,該方法簽名如下:

Object.defineProperty(obj, prop, descriptor)

其中最核心的其實(shí)是 descriptor —— 屬性描述符 。

屬性描述符總共分兩種:數(shù)據(jù)描述符(Data descriptor)和 訪問器描述符(Accessor descriptor)。

描述符必須是兩種形式之一,但不能同時(shí)是兩者。

比如 數(shù)據(jù)描述符

Object.getOwnPropertyDescriptor(user,"name");

// 輸出
/**
{
  "value": "張三",
  "writable": true,
  "enumerable": true,
  "configurable": true
}
**/

還有 訪問器描述符

var anim = { 
  get age() { return 5; } 
};
Object.getOwnPropertyDescriptor(anim, "age");
// 輸出
/**
{
   configurable: true,
   enumerable: true,
   get: /*the getter function*/,
   set: undefined
 }
**/

具體可參考 StackOverflow 上的問答 What is a descriptor? ;

接下來,我們一起來看一下 babel 中到底是如何實(shí)現(xiàn) @ 語法的?

3、搭建裝飾器的 babel 示例

在理解屬性描述符的基礎(chǔ)上,我們就可以去看看 babel 對(duì)于裝飾器 @ 語法的內(nèi)部實(shí)現(xiàn)了。

就拿 MobX 官方的示例 來講:

import { observable, computed, action } from "mobx";

class OrderLine {
    @observable price = 0;
    @observable amount = 1;

    @computed get total() {
        return this.price * this.amount;
    }
    
    @action.bound
    increment() {
        this.amount++ // "this" 永遠(yuǎn)都是正確的
    }
}

我們并不是真正想要運(yùn)行上面那段代碼,而是想看一下 babel 通過裝飾器插件,把上面那段代碼中的 @ 語法轉(zhuǎn)換成什么樣子了。

運(yùn)行這段代碼需要搭建 babel 環(huán)境,所以直接扔到瀏覽器運(yùn)行會(huì)報(bào)錯(cuò)的。按照官方文檔 如何(不)使用裝飾器 中的提示,需要借助 babel-preset-mobx 插件,這是一個(gè)預(yù)設(shè)(preset,相當(dāng)于 babel 插件集合),真正和裝飾器有關(guān)的是插件是 babel-plugin-transform-decorators-legacy。

4、有兩種方式看轉(zhuǎn)換之后的代碼 4.1、 方法一,使用 babel 在線工具

放到 babel 在線工具,粘貼現(xiàn)有的示例代碼會(huì)報(bào)錯(cuò),不過 babel 給出了友好的提示,因?yàn)槭褂玫搅搜b飾器語法,需要安裝 babel-plugin-transform-decorators-legacy:

我們點(diǎn)擊左下方的 Add Plugin 按鈕,在彈出的搜索框里輸入關(guān)鍵字 decorators-legacy,選擇這個(gè)插件就可以:

選完插件之后,代碼就會(huì)成功轉(zhuǎn)譯:

底下會(huì)提示 require is not defined 錯(cuò)誤,這個(gè)錯(cuò)誤并不影響你分析裝飾器的語法,因?yàn)橛?@ 符號(hào)部分都已經(jīng)轉(zhuǎn)換成 ES5 語法了,只是這個(gè)報(bào)錯(cuò)無法讓這段示例代碼運(yùn)行起來。

這是因?yàn)?Babel 只是將最新的 ES6 語法“翻譯”成各大瀏覽器支持比較好的 ES5 語法,但模塊化寫法(require語句)本身就不是 ECMAScript 的標(biāo)準(zhǔn),而是產(chǎn)生了其他的模塊化寫法標(biāo)準(zhǔn),例如 CommonJS,AMD,UMD。因此 Babel 轉(zhuǎn)碼模塊化寫法后在瀏覽器中還是無法運(yùn)行,此時(shí)可以考慮放到 Webpack 這種自動(dòng)化構(gòu)建工具環(huán)境中,此時(shí) Webpack 是支持模塊化寫法的

如果有強(qiáng)迫癥的同學(xué),非得想要這段代碼運(yùn)行起來,可以參考下述的 方法二。

4.2、方法二,使用 demo 工程

官方提供了 mobx-react-boilerplate,clone 下來之后直接:

npm install
npm start
說明:package.json 中的 dependencies 字段比較陳舊了,可以自己手動(dòng)更新到最新版本

打開控制臺(tái)就可以看到 bundle.js 文件了:

這樣,我們就可以直接在 index.js 中粘貼我們需要的代碼,因?yàn)榛?Webpack 打包,所以示例代碼是可以運(yùn)行的。

5、分析轉(zhuǎn)換之后的代碼邏輯

上述兩種方法因?yàn)槎际鞘褂猛粋€(gè)裝飾器轉(zhuǎn)換插件 babel-plugin-transform-decorators-legacy,所以裝飾器語法部分轉(zhuǎn)換后的代碼是一樣的。

比如針對(duì) price 屬性的裝飾器語法:

@observable price = 0;

經(jīng)過 babel 轉(zhuǎn)譯之后:

var _descriptor = _applyDecoratedDescriptor(
    _class.prototype,
    "price",
    [_mobx.observable],
    {
      enumerable: true,
      initializer: function initializer() {
        return 0;
      }
    }
  )

而對(duì)于 total 方法的裝飾器語法:

@computed get total() {
    return this.price * this.amount;
}

經(jīng)過 babel 轉(zhuǎn)譯之后則為:

_applyDecoratedDescriptor(
  _class.prototype,
  "total",
  [_mobx.computed],
  Object.getOwnPropertyDescriptor(_class.prototype, "total"),
  _class.prototype
);

可以看到關(guān)鍵是使用了 _applyDecoratedDescriptor 方法。接下來我們著重分析這個(gè)方法。

6、關(guān)鍵是 _applyDecoratedDescriptor 方法

該函數(shù)簽名為:

function _applyDecoratedDescriptor(
  target,
  property,
  decorators,
  descriptor,
  context
)

具體的用法,以 price 屬性為例,我們可以獲取對(duì)應(yīng)的實(shí)參:

target_class.prototype ,即 OrderLine.prototype

property:即字符串 "price"

decorators:在這里是 [_mobx.observable](不同的修飾符裝飾器是不一樣的,比如使用 @computed 修飾的 total 方法,就是 [_mobx.computed]),是長度為 1 的數(shù)組,具體的 observable 方法將在下一篇文章詳細(xì)講,就是 createObservable

descriptor:即屬性描述符,屬性成員(比如 price)會(huì)有 initializer 屬性,而方法成員(比如 total) 則不會(huì)有這個(gè)屬性,用這個(gè)來區(qū)分這兩種不同屬性描述符。

{
  enumerable: true,
  initializer: function initializer() {
    return 0;
  }
}

context:就是運(yùn)行上下文,一般來講對(duì)數(shù)據(jù)屬性的裝飾則為 null,對(duì)方法屬性則是 _class.prototype;

看完函數(shù)簽名,我們繼續(xù)看函數(shù)內(nèi)容:

這幾行代碼沒啥難度,就是我們熟悉的 屬性描述符 相關(guān)的內(nèi)容:

圖中標(biāo)注 ① ,表示返回的 desc 變量就是我們熟悉的 屬性描述符。因此,該 _applyDecoratedDescriptor 的作用就是根據(jù)入?yún)⒎祷鼐唧w的描述符。

如果是屬性成員(比如price),就將返回的描述符就可以傳給 _initDefineProp (相當(dāng)于 Object.defineProperty)應(yīng)用到原來的屬性中去了,從而起到了 裝飾 作用。

圖中標(biāo)注 ② ,表示對(duì)于方法成員(比如 total)則直接應(yīng)用 Object.defineProperty 方法(當(dāng)是方法成員時(shí),desc 是沒有 initializer 屬性的),同時(shí)令 desc = null,從后續(xù)的應(yīng)用來看并不會(huì)和 _initDefineProp 方法搭配使用

對(duì)于圖中標(biāo)注 ③ ,我們具體看decorators 在其中發(fā)揮的作用,典型的函數(shù)式編程手法:

首先整體上來看,是一個(gè)循環(huán)語句。假如我們傳入的 decorators[a, b, c],那么上面的代碼相當(dāng)于應(yīng)用公式 a(b(c(property))),也就是裝飾器 c 先裝飾屬性 property,隨后再疊加裝飾器 b 的作用,最后疊加裝飾器 a。以 price 屬性為例,由于只有一個(gè)裝飾器(@observable),所以只應(yīng)用了 [_mobx.observable] 這一個(gè)裝飾器。

其次局部來看,裝飾器具體應(yīng)用表達(dá)式是 decorator(target, property, desc) ,其函數(shù)簽名和 Object.defineProperty 是一模一樣。通過圖中標(biāo)注 ③ 我們可以理解,當(dāng)我們寫裝飾器函數(shù)函數(shù)時(shí),函數(shù)的定義入?yún)⒈仨毷?(target, name, descriptor) 這樣的,同時(shí)該函數(shù)必須要返回屬性描述符。(可以停下來去翻翻看自己寫裝飾器函數(shù)的那些例子)

至此我們已經(jīng)掌握了 babel 轉(zhuǎn)換 @ 語法的精髓 —— 創(chuàng)建了 _applyDecoratedDescriptor 方法,從而依次應(yīng)用你所定義的裝飾器方法,而且也明白了自定義的裝飾器方法的函數(shù)簽名必須是 (target, name, descriptor) 的。

總結(jié)一下這個(gè) babel 插件對(duì)于裝飾器語法 @ 所做的事情:

通過 ast 分析,將 @ 語法轉(zhuǎn)換成 _applyDecoratedDescriptor 方法的應(yīng)用

_applyDecoratedDescriptor 方法就是一個(gè)循環(huán)應(yīng)用裝飾器的過程

那么接下來我們回到主題,mobx 如果不使用 babel 轉(zhuǎn)譯,那該如何實(shí)現(xiàn)類似于上述裝飾器的語法呢?

7、不用裝飾器語法,mobx 提供了等價(jià)寫法

很顯然,MobX 不能實(shí)現(xiàn)(也沒有必要)ast 分析將 @ 語法轉(zhuǎn)換掉的功能,所以只能提供 循環(huán)應(yīng)用裝飾器 的這方面的功能。

為達(dá)到這個(gè)目的,MobX 4.x 版本相對(duì) 3.x 等以前版本多了 decorate API 方法。

官方文檔 如何(不)使用裝飾器 所言,使用裝飾器 @ 語法等價(jià)于使用 decorate 方法,即改寫成如下形式:

import { observable, computed, decorate, action } from "mobx";

class OrderLine {
    price = 0;
    amount = 1;

    get total() {
        return this.price * this.amount;
    }
}
decorate(OrderLine, {
    price: observable,
    amount: observable,
    total: computed,
    increment: action.bound
})
3.x 以前的版本因?yàn)闆]有 decorate 方法,所以是借助 extendObservable 方法實(shí)現(xiàn)的,具體見文檔 在ES5、ES6和ES.next環(huán)境下使用 MobX

我們翻開 decorate 源碼,該函數(shù)聲明是:

decorate(thing, decorators)

thing:需要被裝飾的原始對(duì)象;

decorators:裝飾器配置對(duì)象,是一個(gè) key/value 形式的對(duì)象, key 是屬性名,value 就是具體的裝飾器函數(shù)(比如 observable、computedaction.bound 這樣具體的裝飾器有效函數(shù))

摘出核心語句:

可以看去的確就是一個(gè) for 循環(huán),然后依次應(yīng)用 decorator,這恰好就是 babel 插件轉(zhuǎn)換后 _applyDecoratedDescriptor 方法所做的事情,因此兩者是等效的。

這樣,就解答了本文開篇提出的第一個(gè)疑問。 @observable、@computer 等裝飾器語法,是和直接使用 decorate 是等效等價(jià)的。

看到這里是不是覺得有點(diǎn)兒不可思議?嗯,事實(shí)上裝飾器應(yīng)用的過程就這么的簡單。你也可以直接將這個(gè) decorate API 方法直接提取到自己的項(xiàng)目中使用,給你的項(xiàng)目增加新的 feature。

解答完第一個(gè)問題,我們繼續(xù)講本文開頭提出的另一個(gè)問題:MobX 中的 enhancer 是什么概念?

C. 理解 Enhancer 1、Enhancer 概念

Enhancer 這個(gè)概念是 MobX 自己提出的一個(gè)概念,剛接觸到的用戶大多數(shù)會(huì)先蒙圈一會(huì)兒。

學(xué)習(xí)過 MobX 3.x 及以前版本的人可能會(huì)遇到 Modifier 這個(gè)概念,Enhancer 其實(shí)就是 Modifier。

Modifier 在 MobX 3 之前的版本里官方有專門的 文檔 解說。不過到 MobX 4.x 之后官方就刪除了這篇文檔。好在這個(gè)概念是內(nèi)部使用的,修改名字對(duì)外部調(diào)用者沒有啥影響。

Enhancer 從字面上理解是 增強(qiáng)器,其作用就是給原有的對(duì)象 增加額外的功能 —— 這不就是裝飾器的作用么?沒錯(cuò),它是輔助 MobX 中的 @observable 裝飾器功能的。結(jié)合裝飾器,會(huì)更加容易理解這個(gè)概念。

2、Enhancer 和 @observable 的整體關(guān)系

MobX 不是有很多種裝飾器么,比如 @observable、@compute@action,注意 Enhancer 只和 @observable 有關(guān)系,和 @compute@action 是沒啥關(guān)系的。這是因?yàn)?Enhancer 是為觀察值(observable)服務(wù)的,和計(jì)算值(computedValue)和動(dòng)作(Action)沒關(guān)系。

@observable 裝飾器中真正起作用的函數(shù)就是 Enhancer ,你可以將 Enhancer 理解成 @observable 裝飾器有效的那部分??梢杂?"藥物膠囊

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/95931.html

相關(guān)文章

  • 故事解讀 MobX 源碼(五)】 Observable

    摘要:前言初衷以系列故事的方式展現(xiàn)源碼邏輯,盡可能以易懂的方式講解源碼本系列文章用故事解讀源碼一用故事解讀源碼二用故事解讀源碼三用故事解讀源碼四裝飾器和用故事解讀源碼五文章編排每篇文章分成兩大段,第一大段以簡單的偵探系列故事的形式講解所涉及人物場 ================前言=================== 初衷:以系列故事的方式展現(xiàn) MobX 源碼邏輯,盡可能以易懂的方式...

    leeon 評(píng)論0 收藏0
  • 故事解讀 MobX源碼(二)】 computed

    摘要:場景為了多維度掌控嫌疑犯的犯罪特征數(shù)據(jù),你警署最高長官想要獲取并實(shí)時(shí)監(jiān)控張三的貸款數(shù)額存貸比存款和貸款兩者比率的變化。 ================前言=================== 初衷:以系列故事的方式展現(xiàn) MobX 源碼邏輯,盡可能以易懂的方式講解源碼; 本系列文章: 《【用故事解讀 MobX源碼(一)】 autorun》 《【用故事解讀 MobX源碼(二)】...

    Ethan815 評(píng)論0 收藏0
  • 故事解讀 MobX源碼(三)】 shouldCompute

    摘要:最簡單的情況張三的存貸這里我們創(chuàng)建了實(shí)例探長實(shí)例觀察員這個(gè)示例和我們之前在首篇文章用故事解讀源碼一中所用示例是一致的。 ================前言=================== 初衷:以系列故事的方式展現(xiàn) MobX 源碼邏輯,盡可能以易懂的方式講解源碼; 本系列文章: 《【用故事解讀 MobX源碼(一)】 autorun》 《【用故事解讀 MobX源碼(二)】...

    JackJiang 評(píng)論0 收藏0
  • 故事解讀 MobX源碼(一)】 autorun

    摘要:隨后,執(zhí)行官給出一張當(dāng)張三存款發(fā)生變化之時(shí),此機(jī)構(gòu)的運(yùn)作時(shí)序圖的確,小機(jī)構(gòu)靠人力運(yùn)作,大機(jī)構(gòu)才靠制度運(yùn)轉(zhuǎn)。第一條語句創(chuàng)建觀察員第一條語句張三我們調(diào)用的時(shí)候,就創(chuàng)建了對(duì)象,對(duì)象的所有屬性都將被拷貝至一個(gè)克隆對(duì)象并將克隆對(duì)象轉(zhuǎn)變成可觀察的。 ================前言=================== 初衷:網(wǎng)上已有很多關(guān)于 MobX 源碼解讀的文章,但大多閱讀成本甚高。...

    qieangel2013 評(píng)論0 收藏0
  • Mobx 源碼初探 - observable(一)

    摘要:此時(shí)為空對(duì)象當(dāng)構(gòu)造函數(shù)原型對(duì)象上存在屬性,則執(zhí)行下面代碼。屬性名傳遞進(jìn)來的函數(shù)描述符構(gòu)造函數(shù)原型對(duì)象此時(shí)為最后通過調(diào)用函數(shù)為屬性生成描述符。初始值獲取之后,調(diào)用方法,傳入構(gòu)造函數(shù)原型對(duì)象屬性名初始值和。 observable 同時(shí)支持 decorator 方式和方法調(diào)用方式。 // 示例 @observable name = 張三; const temp = observable.bo...

    renweihub 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<