[源碼學(xué)習(xí)]前端緩存工具fast-memoize和nano-memoize

至今天(2018年9月7日)酱鸭,這2個(gè)工具的實(shí)現(xiàn)源碼思想是極其相似的,基本上垛吗,只要閱讀了其中一個(gè)源碼凹髓,也就了解了另外一個(gè)的實(shí)現(xiàn)。

fast-memoize導(dǎo)圖:

image

初識(shí)

大概說(shuō)說(shuō)它們的實(shí)現(xiàn)思路:

  1. 定義緩存結(jié)構(gòu)职烧,其中fast使用了無(wú)prototype的對(duì)象扁誓,nano使用了普通對(duì)象
  2. 定義序列化方法:當(dāng)檢測(cè)到是單參數(shù)時(shí)蚀之,都是選擇JSON.stringify蝗敢,而多個(gè)參數(shù),兩者有不同(后面再說(shuō))足删。
  3. 定義策略:也就是緩存的具體方法寿谴,其實(shí)很簡(jiǎn)單,就是對(duì)當(dāng)前緩存結(jié)構(gòu)查找失受,找到就返回讶泰,找不到就重新運(yùn)行,
    兩者都使用了bind方法注入?yún)?shù)拂到,可以省去運(yùn)行時(shí)再去查找參數(shù)痪署。

接著分析兩者的異同:

相同處:

  • 都使用了JSON.stringify作為序列化方法,因?yàn)樗窃摹?/li>
  • 都對(duì)返回的緩存函數(shù)進(jìn)行了參數(shù)注入(這是一個(gè)極大提升性能的方法)兄旬。
  • 對(duì)單參數(shù)還是多參數(shù)的判斷都是使用func.length(形參的數(shù)量判斷)狼犯,因?yàn)?code>func.length比arguments.length這種動(dòng)態(tài)判斷性能會(huì)好很多。

不同點(diǎn):

  • fast使用了無(wú)prototype的對(duì)象nano使用了普通對(duì)象(這一點(diǎn)性能上相差不多)悯森。
  • 當(dāng)遇到多個(gè)參數(shù)時(shí)宋舷,fast還是繼續(xù)對(duì)arguments進(jìn)行序列化,而nano則復(fù)雜一點(diǎn)瓢姻,它通過(guò)用數(shù)組將每一次多個(gè)參數(shù)保存起來(lái)祝蝠,
    后續(xù)通過(guò)遍歷每個(gè)參數(shù)進(jìn)行全等對(duì)比===,判斷是否從緩存調(diào)取結(jié)果幻碱。
  • 同樣是多個(gè)參數(shù)绎狭,nano增加了一個(gè)參數(shù)max,可以讓用戶自定義需要進(jìn)行對(duì)比參數(shù)的長(zhǎng)度收班。

深入

接著看下第二點(diǎn)不同點(diǎn)的源碼:
主要看nano-memoize

function multiple(f,k,v,eq,change,max=0,...args) {
      // 用來(lái)儲(chǔ)存i(當(dāng)前對(duì)比的參數(shù)索引)和緩存值
      const rslt = {};
      // k是一個(gè)專(zhuān)門(mén)存放多個(gè)參數(shù)的數(shù)組 格式類(lèi)似
      // [[...args],[...args],[...args]...]
      for(let i=0;i<k.length;i++) { // an array of arrays of args
        let key = k[i];
        // 判斷是否需要使用max
        if(max) { key = key.slice(0,max); }
        // 當(dāng)前長(zhǎng)度相等或者有max值坟岔,開(kāi)始進(jìn)行對(duì)比
        if(key.length===args.length || (max && key.length<args.length)) {
          // 獲取長(zhǎng)度
          const max = key.length - 1;
          for(let j=0;j<=max;j++) {
            // 如果發(fā)現(xiàn)不等谒兄,直接跳出
            if(!eq(key[j],args[j])) { break; } // go to next key if args don't match
            // 當(dāng)?shù)搅俗詈笠豁?xiàng)都沒(méi)跳出 說(shuō)明參數(shù)相同
            if(j===max) { // the args matched
              // 記錄當(dāng)前索引
              rslt.i = i;
              // 調(diào)用當(dāng)前參數(shù)的緩存
              rslt.v = v[i]; // get the cached value
            }
          }
        }
      }
      // 如果有i 說(shuō)明是調(diào)用緩存摔桦,如果沒(méi)有i,則添加緩存
      const i = rslt.i>=0 ? rslt.i : v.length;
      if(change) { change(i); }
      // 如果緩存不存在就執(zhí)行func承疲,存在直接返回緩存
      return typeof rslt.v === "undefined" ? v[i] = f.call(this,...(k[i] = args)) : rslt.v;
    }

可以看出邻耕,這是通過(guò)2次遍歷,對(duì)[[...args],[...args],[...args]...]這樣一種結(jié)構(gòu)比較燕鸽,外層遍歷判斷l(xiāng)ength兄世,
length相等才會(huì)進(jìn)入內(nèi)層遍歷,內(nèi)層遍歷就是逐個(gè)判斷了啊研。

// 注入?yún)?shù)御滩,提升性能
f = multiple.bind(
        this,
        fn,
        k,
        v,
        // 逐個(gè)判斷方式默認(rèn)為 ===
        equals || ((a,b) => a===b), // default to just a regular strict comparison
        (maxAge ? change.bind(this,v): null), // turn change logging on and bind to arg cache v
        maxArgs
      );

上面一段則是參數(shù)注入方式和默認(rèn)的對(duì)比方式。

總結(jié)

一個(gè)表格總結(jié)兩者最大不同党远,假設(shè):

  • 忽略===的執(zhí)行時(shí)間
  • 使用的參數(shù)分為 引用相同 和 引用不同(但是深比較都為true)
    例如:{x:1}{x:1}
耗時(shí)操作 多個(gè)參數(shù)(引用相同) 多個(gè)參數(shù)(引用不同)
狀態(tài) 首次運(yùn)行 后續(xù)運(yùn)行 首次運(yùn)行 后續(xù)運(yùn)行
fast 序列化+運(yùn)行函數(shù) 序列化比較 序列化+運(yùn)行函數(shù) 序列化比較
nano 運(yùn)行函數(shù) 0(===比較) 運(yùn)行函數(shù) 運(yùn)行函數(shù)(===比較失敗)

源碼(帶注釋)倉(cāng)庫(kù)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末削解,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子沟娱,更是在濱河造成了極大的恐慌氛驮,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件济似,死亡現(xiàn)場(chǎng)離奇詭異矫废,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)砰蠢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)蓖扑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人台舱,你說(shuō)我怎么就攤上這事律杠。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵俩功,是天一觀的道長(zhǎng)幻枉。 經(jīng)常有香客問(wèn)我,道長(zhǎng)诡蜓,這世上最難降的妖魔是什么熬甫? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮蔓罚,結(jié)果婚禮上椿肩,老公的妹妹穿的比我還像新娘。我一直安慰自己豺谈,他們只是感情好郑象,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著茬末,像睡著了一般厂榛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上丽惭,一...
    開(kāi)封第一講書(shū)人閱讀 49,829評(píng)論 1 290
  • 那天击奶,我揣著相機(jī)與錄音,去河邊找鬼责掏。 笑死柜砾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的换衬。 我是一名探鬼主播痰驱,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼瞳浦!你這毒婦竟也來(lái)了担映?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤术幔,失蹤者是張志新(化名)和其女友劉穎另萤,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體诅挑,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡四敞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拔妥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片忿危。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖没龙,靈堂內(nèi)的尸體忽然破棺而出铺厨,到底是詐尸還是另有隱情缎玫,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布解滓,位于F島的核電站赃磨,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏洼裤。R本人自食惡果不足惜邻辉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望腮鞍。 院中可真熱鬧值骇,春花似錦、人聲如沸移国。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)迹缀。三九已至使碾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間裹芝,已是汗流浹背部逮。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工娜汁, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嫂易,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓掐禁,卻偏偏與公主長(zhǎng)得像怜械,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子傅事,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容