<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>

          《面試題系列》淺談裝飾器

          共 3225字,需瀏覽 7分鐘

           ·

          2022-02-27 22:28

          一個(gè)朋友一面遇到的面試題,還挺有意思,簡單分享下。由一個(gè)簡單的場(chǎng)景,把一些基礎(chǔ)知識(shí)點(diǎn)串了下。

          實(shí)現(xiàn)函數(shù)緩存

          假設(shè)有函數(shù) calculate,對(duì)于相同的入?yún)ⅲ偸悄芊祷叵嗤慕Y(jié)果。但每次計(jì)算很耗性能,希望簡單設(shè)計(jì)一個(gè)緩存的方案。

          function?calculate?(a)?{
          ??const?b?=?耗性能的計(jì)算(a)
          ??return?b;
          }

          第一種最直觀的實(shí)現(xiàn)

          緩存,顧名思義,將結(jié)果存起來,再次調(diào)用,如果命中緩存,直接從緩存取,否則返回結(jié)果,并緩存起來。

          let?cache?=?new?Map();
          function?calculate?(a)?{
          ??const?b?=?耗性能的計(jì)算(a);
          ??if?(cache.has[a])?return?cache.get(a);
          ??cache.set(a,?b);
          ??return?b;
          }

          但是這樣對(duì)原方法有侵入性,假如現(xiàn)在有很多方法都需要加緩存呢?

          第二種裝飾器的實(shí)現(xiàn)

          function?decorator(targetFn)?{
          ??let?cache?=?new?Map();
          ??
          ??return?function?(a)?{
          ????if?(cache.has(a))?return?cache.get(a);
          ????
          ????const?b?=?targetFn(a);
          ????cache.set(a,?b);
          ????
          ????return?b;
          ??}
          }

          其實(shí)代碼跟第一種幾乎一樣,只是換了種思路,但是復(fù)用性更強(qiáng)了。假如 calculate 函數(shù)的入?yún)⑹菍?duì)象,并且要考慮緩存回收的問題,那么換成 WeakMap 就比較合適。

          第三種解決 this 丟失的實(shí)現(xiàn)

          第二種會(huì)有一個(gè)問題,假如 calculate 函數(shù)是這樣的。

          const?case?=?{
          ??easy()?{
          ????return?200;
          ??},
          ??
          ??calculate(a)?{
          ????const?b?=?耗性能的計(jì)算(a);
          ????return?this.easy()?+?b;
          ??}
          }

          //?假如直接使用第二種的實(shí)現(xiàn):
          case.calculate?=?decorator(case.calculate);

          //?此時(shí)調(diào)用會(huì)報(bào)錯(cuò)
          //?嚴(yán)格模式下:?Uncaught?TypeError:?Cannot?read?properties?of?undefined?(reading?'easy')
          //?非嚴(yán)格模式下:Uncaught?TypeError:?this.easy?is?not?a?function
          case.calculate(0);

          //?既然是?this?丟失了,那么我們改寫一下第二種的實(shí)現(xiàn)
          function?decorator(targetFn)?{
          ??let?cache?=?new?Map();
          ??
          ??return?function?(a)?{
          ????if?(cache.has(a))?return?cache.get(a);
          ????
          ????//?const?b?=?targetFn(a);
          ????//?上面注釋的換成下面這行,指定?this?調(diào)用就好了
          ????const?b?=?targetFn.call(this,?a);
          ????
          ????cache.set(a,?b);
          ????
          ????return?b;
          ??}
          }

          這一種已經(jīng)挺完整了,像 lodash 中的 debounced 方法,就是裝飾器這種結(jié)構(gòu)。

          第四種解決屬性丟失的實(shí)現(xiàn)

          還有一個(gè)問題,沒解決。假如被裝飾函數(shù)有自己的一些屬性,那么經(jīng)過 decorator 包裝之后,這些屬性會(huì)丟失。

          function?calculate?()?{}
          calculate.tag?=?'water';

          const?calculate?=?decorator(calculate);
          //?calculate.tag?為?undefined

          我們可以改造 decorator 函數(shù),利用 Proxy 來實(shí)現(xiàn)。

          //?假設(shè)?calculate?是這個(gè)樣子
          function?calculate(x)?{
          ??return?x?+?1;
          }

          calculate.tags?=?'water';

          function?decorator(targetFn)?{
          ??let?cache?=?new?Map();

          ??//?裝飾器部分改用?Proxy?來做代理攔截
          ??return?new?Proxy(targetFn,?{
          ????apply(target,?thisArgs,?args)?{
          ??????if?(cache.has(args[0]))?return?cache.get(args[0]);

          ??????const?b?=?target.apply(thisArgs,?args);
          ??????cache.set(args[0],?b);

          ??????return?b;
          ????}
          ??})
          }

          calculate?=?decorator(calculate);
          console.log(calculate.tags);?//?water

          裝飾器模板

          經(jīng)過上面一系列的流程,我們可以簡單總結(jié)出一個(gè)裝飾器模板。

          function?decoratorTemplate?(targetFn)?{
          ??//?輔助變量(閉包特性)
          ??
          ??//?Proxy?作代理攔截
          ??return?new?Proxy(targetFn,?{
          ????apply(target,?thisArg,?args)?{
          ??????//?apply?對(duì)函數(shù)進(jìn)行包裝
          ??????target.apply(thisArg,?args);
          ????}
          ??});
          }

          比如,我們想實(shí)現(xiàn)函數(shù)執(zhí)行完成之后,打印一條 log。

          function?logCompleted?()?{};

          function?decoratorTemplate?(targetFn)?{
          ??//?輔助變量(閉包特性)
          ??
          ??//?Proxy?作代理攔截
          ??return?new?Proxy(targetFn,?{
          ????apply(target,?thisArg,?args)?{
          ??????//?apply?對(duì)函數(shù)進(jìn)行包裝
          ??????target.apply(thisArg,?args);
          ??????console.log('logs:?completed');
          ????}
          ??});
          }

          logCompleted?=?decoratorTemplate(logCompleted);
          logCompleted();?//?logs:?completed

          再比如,我們想統(tǒng)計(jì)函數(shù)執(zhí)行的次數(shù)。

          function?logCompleted?()?{};

          function?decoratorTemplate?(targetFn)?{
          ??//?輔助變量(閉包特性)
          ??let?count?=?0;
          ??
          ??//?Proxy?作代理攔截
          ??return?new?Proxy(targetFn,?{
          ????apply(target,?thisArg,?args)?{
          ??????//?apply?對(duì)函數(shù)進(jìn)行包裝
          ??????target.apply(thisArg,?args);
          ??????console.log(`logs:?${count}`);
          ??????count?+=?1;
          ????}
          ??});
          }

          logCompleted?=?decoratorTemplate(logCompleted);
          logCompleted();?//?logs:?0
          logCompleted();?//?logs:?1

          小結(jié)

          近期有不少朋友在面試,盡量把一些有意思的題目,跟大家分享下。

          像這種由場(chǎng)景展開的面試題個(gè)人覺得比較好,一上來就直接問概念的那種,不知道小伙伴遇到過沒?留言區(qū)歡迎交流64d4090c9a1700906582f65c03682f9b.webp


          瀏覽 30
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  久久中文字幕7区 | 苍井空在线一区二区 | 日韩无码123区 | AV无码电影 | 性爱激情五月 |