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

          JavaScript 函數(shù)式編程

          共 3815字,需瀏覽 8分鐘

           ·

          2021-06-19 12:03

          來(lái)源 | https://github.com/cheogo/learn-javascript


          函數(shù)式編程是一種編程范式,記得在剛學(xué)編程時(shí)從面向過(guò)程編程 轉(zhuǎn)換到 面向?qū)ο缶幊?nbsp;時(shí)的觸動(dòng),了解 函數(shù)式編程 或許會(huì)給你一個(gè)最初的驚喜。
          函數(shù)式編程是一個(gè)很大的命題,在本文中將介紹幾個(gè)基本概念:純函數(shù)、柯里化(curry)、組合(compose)、容器(container)、函子(functor),希望能激起你對(duì)它的興趣。

          如何實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用

          先讓我們忘掉上面那些奇怪的概念,讓我們看一個(gè)貫徹全文的實(shí)例,如何實(shí)現(xiàn)一個(gè)鏈?zhǔn)秸{(diào)用。
          var Container = function(x) {  this.__value = x;}
          Container.of = function(x) { return new Container(x); };
          Container.prototype.map = function(f){ return Container.of(f(this.__value))}

          上述代碼實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的鏈?zhǔn)秸{(diào)用,讓我們看看如何使用它。

          Container.of(3); // Container {__value: 3}Container.of(4); // Container {__value: 4}
          var add1 = function (num) { return num + 1 };var add2 = function (num) { return num + 2 };
          Container.of(3).map(add1).map(add2) // Container {__value: 6}Container.of(4).map(add2).map(add2).map(add2) // Container {__value: 10}

          在這個(gè)實(shí)例中出現(xiàn)的 Container 是一個(gè)容器,通過(guò) Container.of 來(lái)實(shí)例化保存值到 this.__value 。

          add1、add2 都是 純函數(shù),我們通過(guò) map 函數(shù)來(lái)操作容器內(nèi)的值,我們把 Container 看作數(shù)據(jù)結(jié)構(gòu),這種數(shù)據(jù)結(jié)構(gòu)可以通過(guò) map 操作,那么它就叫 functor。

          純函數(shù)

          什么是純函數(shù):純函數(shù)是這樣一種函數(shù),即相同的輸入,永遠(yuǎn)會(huì)得到相同的輸出,而且沒(méi)有任何可觀察的副作用。

          比如 slice 和 splice,這兩個(gè)函數(shù)的作用并別無(wú)二致。但是我們說(shuō) slice 符合純函數(shù)的定義是因?yàn)閷?duì)相同的輸入它保證能返回相同的輸出。

          而 splice 的調(diào)用卻會(huì)產(chǎn)生可觀察到的副作用,這個(gè)數(shù)組被永久地改變了。

          var xs = [1,2,3,4,5];
          // 純的xs.slice(0,3); // => [1,2,3]xs.slice(0,3); // => [1,2,3]
          // 不純的xs.splice(0,3); // => [1,2,3]xs.splice(0,3); // => [4,5]

          在函數(shù)式編程中,我們盡量杜絕 splice 這種會(huì)改變數(shù)據(jù)的函數(shù)。我們追求的是 slice 那種可靠的,每次都能返回同樣結(jié)果的函數(shù)。

          再看另一個(gè)例子

          // 不純的var num_1 = 1var add1 = function (num) { return num + num_1 };
          // 純的var add1 = function (num) { return num + 1 };

          在不純的版本中,add1 的結(jié)果將取決于 num_1 這個(gè)可變變量的值。換句話說(shuō),它取決于系統(tǒng)狀態(tài)(system state)。因?yàn)樗肓送獠康沫h(huán)境,從而增加了認(rèn)知負(fù)荷(cognitive load)。

          這種依賴狀態(tài)是影響系統(tǒng)復(fù)雜度的罪魁禍?zhǔn)祝粌H讓它變得不純,而且導(dǎo)致每次我們思考整個(gè)軟件的時(shí)候都痛苦不堪。

          為什么要使用純函數(shù)呢?舉例容易看到的好處:1. 可緩存性,因?yàn)榧兒瘮?shù)對(duì)于相同的輸入有相同的輸出,所以純函數(shù)是可以緩存運(yùn)算結(jié)果的;2. 可移植性,因?yàn)椴粫?huì)受環(huán)境變量等外部狀態(tài)的影響,可以方便移植;3. 可測(cè)試性,無(wú)需配置外部變量,一個(gè)輸入一個(gè)輸出,直接斷言;等等。

          有哪些不純的情況呢?

          1. IO 操作,你不知道你讀取的內(nèi)容會(huì)是怎樣;

          2. 接口請(qǐng)求,你確定返回的內(nèi)容是什么;

          3. dom 操作,引起了副作用;

          4. 甚至連 console.log 都是不純的,因?yàn)樗懈弊饔茫坏鹊取?/span>

          對(duì)于不純的函數(shù)我們盡量把它控制在可控范圍內(nèi)發(fā)生,這個(gè)會(huì)在文章后面提到。

          函數(shù)柯里化

          什么是柯里化(curry)?curry 的概念很簡(jiǎn)單,只傳遞給函數(shù)一部分參數(shù)來(lái)調(diào)用它,讓它返回一個(gè)函數(shù)去處理剩下的參數(shù)

          簡(jiǎn)單的實(shí)例:

          var add = function (x, y) {  return x + y; }add(1, 2)   // 3add(10, 1)  // 11add(10, 2)  // 12add(10, 3)  // 13
          // curryvar add = function(x) { return function(y) { return x + y; };};
          var increment = add(1);var addTen = add(10);
          increment(2); // 3addTen(1); // 11addTen(2); // 12addTen(3); // 13

          我們把 add 函數(shù)通過(guò)柯里化變成了接受部分參數(shù)并返回一個(gè)處理剩余函數(shù)且返回結(jié)果的函數(shù)。在實(shí)際環(huán)境中我們可能用到 ramda 這樣的庫(kù)來(lái)幫助我們實(shí)現(xiàn)柯里化。

          var R = require('ramda');var add = function (x, y) { return x + y; }var addTen = R.curry(add)(10)
          addTen(1); // 11addTen(2); // 12

          柯里化是函數(shù)式編程的工具,他能實(shí)現(xiàn)預(yù)加載函數(shù)、分步取值、避免重復(fù)傳參、鎖定函數(shù)運(yùn)行環(huán)境等等功能。

          函數(shù)組合

          這就是組合(compose)

          // 簡(jiǎn)單實(shí)現(xiàn),復(fù)雜實(shí)現(xiàn)可以傳遞多個(gè)函數(shù)用于組合var compose = function(f,g) {  return function(x) {    return f(g(x));  };};

          組合多個(gè)函數(shù)生成一個(gè)新的函數(shù),并且函數(shù)從右往左運(yùn)行。

          var double = function (num) { return num * 2 }var add =  R.curry(function (x, y) { return x + y; })
          var price = compose(double, add(10)) // 通過(guò)成本獲取商品價(jià)格
          price(10) // 40price(20) // 60

          通過(guò)函數(shù)組合我們可以,一次性的合并多個(gè)處理函數(shù),并且可以方便的改變函數(shù)的執(zhí)行順序。

          容器和函子(functor)

          讓我們回顧開(kāi)頭的例子

          var Container = function(x) {  this.__value = x;}
          Container.of = function(x) { return new Container(x); };
          Container.prototype.map = function(f){ return Container.of(f(this.__value))}

          現(xiàn)在我們轉(zhuǎn)換角度,把調(diào)用 Container.of 返回的對(duì)象看作一種數(shù)據(jù)結(jié)構(gòu) Container {__value: 3} ,這種數(shù)據(jù)結(jié)構(gòu)只能使用 map 方法進(jìn)行操作,類似這樣的數(shù)據(jù)結(jié)構(gòu)被稱為 functor。

          這樣做的好處是什么呢?我們能在不離開(kāi) 容器(Container) 的情況下操作容器里面的值,操作完成之后又放回容器。

          我們可以不斷的進(jìn)行這一操作,就像 組合函數(shù) 一樣。這是一種抽象,我們讓容器保存值,并且請(qǐng)求容器通過(guò) map 里的函數(shù)去操作值。

          總結(jié)

          在文章中,提到了 純函數(shù)、柯里化(curry)、組合(compose)、容器(container)、函子(functor),不要看它們很遙遠(yuǎn)其實(shí)已經(jīng)或多或少出現(xiàn)在我們身邊。舉個(gè)例子:尖頭函數(shù)。

          const curryAdd = x => y => x + yconst compose = (f, g) => x => f(g(x))const double = num => num * 2const price = y => compose(double, curryAdd(10))(y)
          console.log(price(0)) // 20

          僅僅幾行代碼就可以體驗(yàn) curry 和 compose 工具。

          本文完~


          學(xué)習(xí)更多技能

          請(qǐng)點(diǎn)擊下方公眾號(hào)

          瀏覽 39
          點(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精品国产综合久久久蜜臀酒店 | 操人视频网站 | 97香蕉视频 | 人人射人人射 |