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

          JS 代碼越來(lái)越難讀了 ...

          共 4030字,需瀏覽 9分鐘

           ·

          2022-05-24 04:28

          今天來(lái)給大家介紹 JavaScript 代碼的一個(gè)新運(yùn)算符:管道運(yùn)算符 |>。

          對(duì)一個(gè)值執(zhí)行連續(xù)操作

          當(dāng)我們?cè)?JavaScript 中對(duì)一個(gè)值執(zhí)行連續(xù)操作(例如函數(shù)調(diào)用)時(shí),目前有兩種基本方式:

          • 將值作為參數(shù)傳遞給具體操作(如果有多個(gè)操作,則嵌套操作),例如:three(two(one(value)));
          • 將函數(shù)作為值上的方法調(diào)用(如果有多個(gè)方法,則為鏈?zhǔn)秸{(diào)用),例如:value.one().two().three()。

          2020JS 狀態(tài)調(diào)查中,“你認(rèn)為 JavaScript 目前缺少什么?“ 問(wèn)題中,希望擁有管道操作符 答案排行第四名。

          看來(lái)大家當(dāng)前對(duì) JS 中連續(xù)操作的寫法還是不太滿意啊。

          首先,如果是嵌套寫法的話,簡(jiǎn)單的嵌套還好,但是當(dāng)嵌套變得很深的時(shí)候就有點(diǎn)難以閱讀了。嵌套的執(zhí)行流程是從右到左移動(dòng)的,而不是我們正常閱讀代碼從左到右的方向。另外,我們?cè)诤芏嗬ㄌ?hào)之間找到一個(gè)位置添加一些參數(shù)也比較困難。比如下面的代碼:

          console.log(
          ??chalk.dim(
          ????`$?${Object.keys(envars)
          ??????.map(envar?=>
          ????????`${envar}=${envars[envar]}`)
          ??????.join('?')
          ????}
          `
          ,
          ????'node',
          ????args.join('?')));

          對(duì)于鏈?zhǔn)秸{(diào)用,只有我們把方法指定為值的實(shí)例方法時(shí)才能用,這讓它具有很大的局限性。當(dāng)然,如果你的庫(kù)設(shè)計(jì)的很好(比如 jQuery) 還是挺好用的。

          管道式編程

          Unix 操作系統(tǒng)有一個(gè)管道機(jī)制,可以把前一個(gè)操作的值傳給后一個(gè)操作。這個(gè)機(jī)制非常有用,使得簡(jiǎn)單的操作可以組合成為復(fù)雜的操作。許多語(yǔ)言都有管道的實(shí)現(xiàn),舉個(gè)簡(jiǎn)單的例子:

          function?capitalize?(str)?{
          ??return?str[0].toUpperCase()?+?str.substring(1);
          }
          function?hello?(str)?{
          ??return?str?+?'?Hello!';
          }

          上面是兩個(gè)簡(jiǎn)單的函數(shù),想要嵌套執(zhí)行,傳統(tǒng)寫法和管道寫法分別如下:

          //?傳統(tǒng)的寫法
          exclaim(hello('conardli'))
          //?"Conardli?Hello!"

          //?管道的寫法
          'conardli'
          ??|>?capitalize
          ??|>?hello
          //?"Conardli?Hello!"

          兩個(gè)互相競(jìng)爭(zhēng)的提案

          關(guān)于管道運(yùn)算符,目前在 ES 中有兩個(gè)相互競(jìng)爭(zhēng)的提案:

          Microsoft 提出的 F# :是一種函數(shù)式編程語(yǔ)言,其核心基于 OCaml,這個(gè)運(yùn)算符可以很方便的寫出柯里化風(fēng)格的代碼。

          Meta 提出的 Hack:大致是 PHP 的靜態(tài)類型版本。這個(gè)管道運(yùn)算符專注于柯里化函數(shù)以外的語(yǔ)言特性。

          目前來(lái)看,Meta 提出的 Hack 應(yīng)該更收社區(qū)的歡迎,Microsoft 提出的 F# 已經(jīng)多次被 TC39 打回去了。不過(guò)不用擔(dān)心,F# 的優(yōu)勢(shì)后續(xù)也可能會(huì)引入 Hack 中。

          下面我們分別來(lái)看看兩個(gè)提案的用法吧。

          Hack 管道運(yùn)算符

          下面是一個(gè) Hack 管道運(yùn)算符 |> 的簡(jiǎn)單示例:

          'ConardLi'?|>?console.log(%)??//?ConardLi

          管道運(yùn)算符 |> 的左側(cè)是一個(gè)表達(dá)式,它被計(jì)算并成為特殊變量 % 的值。我們可以在右側(cè)使用該變量。返回右側(cè)的執(zhí)行結(jié)果。前面的例子等價(jià)于:

          console.log('ConardLi')?//?ConardLi

          下面還有一些和其他寫法配合的例子:

          value?|>?someFunction(1,?%,?3)?//?function?calls
          value?|>?%.someMethod()?//?method?call
          value?|>?%?+?1?//?operator
          value?|>?[%,?'b',?'c']?//?Array?literal
          value?|>?{someProp:?%}?//?object?literal
          value?|>?await?%?//?awaiting?a?Promise
          value?|>?(yield?%)?//?yielding?a?generator?value

          下面我們?cè)賮?lái)看個(gè)更復(fù)雜點(diǎn)的例子,一個(gè)嵌套函數(shù)調(diào)用:

          const?y?=?h(g(f(x)));

          Hack pipe 操作符可以讓我們更好地表達(dá)這段代碼的意思:

          const?y?=?x?|>?f(%)?|>?g(%)?|>?h(%);

          這段代碼更符合我們常規(guī)的編碼思想,代碼從左到右依次執(zhí)行:f、g、h

          F# 管道運(yùn)算符

          F# 管道運(yùn)算符與 Hack 管道運(yùn)算符大致相似。但是,它沒(méi)有特殊變量 %。相反,運(yùn)算符右側(cè)的函數(shù)并會(huì)直接應(yīng)用于其左側(cè)。因此,以下兩個(gè)表達(dá)式是等價(jià)的:

          'ConardLi'?|>?console.log

          console.log('ConardLi')

          因此 F# 管道運(yùn)算符更適合單參數(shù)的函數(shù),下面三個(gè)函數(shù)是等價(jià)的:

          const?y?=?h(g(f(x)));?//?no?pipe
          const?y?=?x?|>?f(%)?|>?g(%)?|>?h(%);?//?Hack?pipe
          const?y?=?x?|>?f?|>?g?|>?h;?//?F#?pipe

          在這種情況下,Hack pipeF# pipe 更冗長(zhǎng)。

          但是,如果是多參數(shù)的情況下,F# pipe 的寫法就要復(fù)雜一點(diǎn)了:

          5?|>?add2(1,?%)?//?Hack?pipe
          5?|>?$?=>?add2(1,?$)?//?F#?pipe

          可以看到,F# pipe 還要多寫一個(gè)匿名函數(shù),這顯然相對(duì)與 Hack pipe 來(lái)講缺失了一些靈活性。這可能也是大家更傾向于 Hack pipe 的原因。

          管道運(yùn)算符的一些實(shí)際用例

          嵌套函數(shù)調(diào)用的扁平寫法

          JavaScript 標(biāo)準(zhǔn)庫(kù)創(chuàng)建的所有迭代器都有一個(gè)共同的原型。這個(gè)原型是不能直接訪問(wèn)的,但我們可以像這樣檢索它:

          const?IteratorPrototype?=
          ??Object.getPrototypeOf(
          ????Object.getPrototypeOf(
          ??????[][Symbol.iterator]()
          ????)
          ??)
          ;

          使用管道運(yùn)算符,代碼會(huì)更容易理解一些:

          const?IteratorPrototype?=
          ??[][Symbol.iterator]()
          ??|>?Object.getPrototypeOf(%)
          ??|>?Object.getPrototypeOf(%)
          ;

          后期處理

          看看下面的代碼:

          function?myFunc()?{
          ??//?···
          ??return?conardLi.someMethod();
          }

          如果現(xiàn)在我們想在函數(shù)返回前對(duì)返回值做一些其他的操作,我們應(yīng)該怎么辦呢?

          在以前我們肯定要定義一個(gè)臨時(shí)變量或者在函數(shù)外側(cè)再包一個(gè)函數(shù),使用管道運(yùn)算符,我們可以這樣做:

          function?myFunc()?{
          ??//?···
          ??return?theResult?|>?(console.log(%),?%);?//?(A)
          }

          在下面的代碼中,我們后處理的值是一個(gè)函數(shù) — 我們可以向它添加一個(gè)屬性:

          const?testPlus?=?()?=>?{
          ??assert.equal(3+4,?7);
          }?|>?Object.assign(%,?{
          ??name:?'Test?the?plus?operator',
          });

          前面的代碼等價(jià)于:

          const?testPlus?=?()?=>?{
          ??assert.equal(3+4,?7);
          }
          Object.assign(testPlus,?{
          ??name:?'Testing?+',
          });

          我們也可以像這樣使用管道運(yùn)算符:

          const?testPlus?=?()?=>?{
          ??assert.equal(3+4,?7);
          }
          |>?(%.name?=?'Test?the?plus?operator',?%)
          ;

          鏈?zhǔn)胶瘮?shù)調(diào)用

          我們可以用 Array 的一些方法例如 .filter().map() 實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用,但是這僅僅是內(nèi)置在數(shù)組里的一些方法,我們沒(méi)辦法通過(guò)庫(kù)引入更多的 Array 方法。

          使用管道運(yùn)算符,我們可以像數(shù)組本身的方法一樣實(shí)現(xiàn)一些其他方法的鏈?zhǔn)秸{(diào)用:

          import?{Iterable}?from?'@rauschma/iterable/sync';
          const?{filter,?map}?=?Iterable;

          const?resultSet?=?inputSet
          ??|>?filter(%,?x?=>?x?>=?0)
          ??|>?map(%,?x?=>?x?*?2)
          ??|>?new?Set(%)
          ;

          最后再回來(lái)看看標(biāo)題的代碼:

          const?regexOperators?=
          ??['*',?'+',?'[',?']']
          ??.map(ch?=>?escapeForRegExp(ch))
          ??.join('')
          ??|>?'['?+?%?+?']'
          ??|>?new?RegExp(%)
          ;

          實(shí)際上就等價(jià)于

          let?_ref;

          const?regexOperators?=
          ??(
          ????(_ref?=?['*',?'+',?'[',?']']
          ??????.map(ch?=>?escapeForRegExp(ch))
          ??????.join('')),?
          ????new?RegExp(`[${_ref}]`)
          ??);

          和引入中間變量相比,管道運(yùn)算符是不是更易于閱讀且簡(jiǎn)潔呢。

          參考

          • https://github.com/tc39/proposal-pipeline-operator
          • https://2ality.com/2022/01/pipe-operator.html


          你喜歡這種寫法么?


          瀏覽 40
          點(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>
                  免费播放婬乱视频 | 91搞搞 | 99视频热| 无码影院在线观看 | 九九九免费在线视频 |