1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        JS 代碼越來越難讀了 ...

        共 6116字,需瀏覽 13分鐘

         ·

        2022-07-04 21:11

        今天來給大家介紹 JavaScript 代碼的一個新運算符:管道運算符 |>

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

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

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

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

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

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

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

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

        管道式編程

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

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

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

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

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

        兩個互相競爭的提案

        關(guān)于管道運算符,目前在 ES 中有兩個相互競爭的提案:

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

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

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

        下面我們分別來看看兩個提案的用法吧。

        Hack 管道運算符

        下面是一個 Hack 管道運算符 |> 的簡單示例:

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

        管道運算符 |> 的左側(cè)是一個表達式,它被計算并成為特殊變量 % 的值。我們可以在右側(cè)使用該變量。返回右側(cè)的執(zhí)行結(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

        下面我們再來看個更復(fù)雜點的例子,一個嵌套函數(shù)調(diào)用:

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

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

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

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

        F# 管道運算符

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

        'ConardLi' |> console.log

        console.log('ConardLi')

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

        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 更冗長。

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

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

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

        管道運算符的一些實際用例

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

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

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

        使用管道運算符,代碼會更容易理解一些:

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

        后期處理

        看看下面的代碼:

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

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

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

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

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

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

        前面的代碼等價于:

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

        我們也可以像這樣使用管道運算符:

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

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

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

        使用管道運算符,我們可以像數(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(%)
        ;

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

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

        實際上就等價于

        let _ref;

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

        和引入中間變量相比,管道運算符是不是更易于閱讀且簡潔呢。

        參考

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

        往期干貨

         26個經(jīng)典微信小程序+35套微信小程序源碼+微信小程序合集源碼下載(免費)

         干貨~~~2021最新前端學(xué)習(xí)視頻~~速度領(lǐng)取

         前端書籍-前端290本高清pdf電子書打包下載


        點贊和在看就是最大的支持??


        瀏覽 58
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            一二三四社区视频在线观看 | 亚洲一区二区三区蜜桃 | 啊好大好爽好舒服 | 麻酥酥在线观看 | 日韩制服丝袜一区二区 | 国产成人综合电影 | 国产精品久久成人123 | 上课忘穿内裤被老师摸到高潮 | 国产精品久久免费视频 | 国产一级毛片国语片 |