函數(shù)式編程

JavaScript函數(shù)式編程

函數(shù)式編程的基礎(chǔ)是一等函數(shù)(函數(shù)在js中作為一等公民)、作用域(詞法作用域,動(dòng)態(tài)作用域)和閉包。
函數(shù)式編程的第一個(gè)概念是高等函數(shù):高等函數(shù)將函數(shù)作為參數(shù),或者將函數(shù)作為返回值。
高等函數(shù)是函數(shù)式編程的基礎(chǔ),幾乎隨處可見高等函數(shù)。

由函數(shù)構(gòu)建函數(shù)

首先討論函數(shù)的構(gòu)建。函數(shù)式編程通過高等函數(shù)和一等函數(shù)構(gòu)建,常用的方法有三種

柯里化

柯里化為每一個(gè)邏輯參數(shù)返回一個(gè)新函數(shù),分為手動(dòng)柯里化和自動(dòng)柯里化。

手動(dòng)柯里化

自己手寫函數(shù)的柯里化版本,有柯里化方向的選擇(向左還是向右)

function div(n ,d) {
  return n / d;
}

function leftCurry(n) {
  return function (d) {
    return n / d;
  }
}

function rightCurry(d) {
  return function (n) {
    return n / d;
  }
}

但這樣很麻煩,每個(gè)函數(shù)都要提供一個(gè)柯里化版本

自動(dòng)柯里化

指我們通過一個(gè)curry函數(shù)生成一個(gè)普通函數(shù)的柯里化版本

比如我們可以寫這樣一個(gè)curry2函數(shù)做到手動(dòng)柯里化中的那種效果

function curry2(func) {
  return function (first) {
    return function (second) {
      return func(first, second);
    };
  };  
}

function div(n, d) {
  return n / d;
}

var leftCurry = curry(div);

這樣實(shí)現(xiàn)的一個(gè)leftCurry跟手動(dòng)柯里化實(shí)現(xiàn)的是一模一樣的;但是自動(dòng)柯里化是用過curry函數(shù)和div函數(shù)構(gòu)建出leftCurry函數(shù)的;這樣做也限定了柯里化的方向;我們可以通過再編寫一個(gè)curry函數(shù)使用另外一種方向來解決這個(gè)問題。

再說一個(gè)自動(dòng)柯里化的用處。這個(gè)柯里化如下:

function curry(func) {
  return function (args) {
    return func(args);
  }
}

直觀上看這個(gè)柯里化有什么用?為什么不直接使用func(args)呢?

這個(gè)柯里化的場景在使用這樣的語句時(shí)格外有用[11, 11, 11, 11].map(parseInt)時(shí)格外有用。這行代碼貌似會(huì)返回[11, 11, 11, 11],但實(shí)際上的返回結(jié)果是[11, NaN, 3, 4]。

這是為什么呢?我們看了map的源碼就能知道,map接收的函數(shù),實(shí)際上是一個(gè)iteratee(item, index, array),也就是說,這行代碼實(shí)際運(yùn)行的是[parseInt(11, 0, array) parseInt(11, 1, array), parseInt(11, 2, array), parseInt(11, 3, array)]。

為了避免給這個(gè)iteratee傳入過多的參數(shù),我們可以通過柯里化返回一個(gè)只接受一個(gè)參數(shù)的函數(shù)

[11, 11, 11, 11].parseInt(curry(parseInt)),這樣我們就能得到理想的結(jié)果[11, 11, 11, 11].

柯里化的缺點(diǎn)

柯里化明顯只適合于有限參數(shù)的函數(shù);如果函數(shù)的參數(shù)過多(當(dāng)然我們杜絕設(shè)計(jì)這樣的函數(shù))或者函數(shù)的參數(shù)未定,那就不適合用柯里化來構(gòu)建函數(shù)。這時(shí)就更適合用partial。

partial

bind的實(shí)現(xiàn)其實(shí)就有partial的寫法;因?yàn)?code>bind在傳遞上下文的時(shí)候,也是可以傳部分參數(shù)的。

Function.prototype.bind = function (context) {
  context = Obejct(context) || window;
  // 保存一部分參數(shù)
  var args = [].slice.call(arguments, 1);
  // ...其他操作

  return function () {
    // 補(bǔ)全參數(shù)
    args = args.concat([].slice.call(arguments));
  }
}

compose

compose就是拼接函數(shù),返回一系列函數(shù)組合后的復(fù)合函數(shù),好比在數(shù)學(xué)里, 把函數(shù) f(), g(), 和 h() 組合起來可以得到復(fù)合函數(shù) f(g(h()))。

遞歸

  1. 遞歸最有名的應(yīng)用就是深克隆
  2. 遞歸操作都應(yīng)該被封裝

函數(shù)式編程的組成

這里有兩個(gè)React講到的概念:純函數(shù),不變性。

純函數(shù)

什么是純函數(shù)?

  1. 返回結(jié)果只由參數(shù)決定,不受外部數(shù)據(jù)的影響
  2. 不改變外部狀態(tài)
  3. 不受Math.random,Date.now影響,沒有this,沒有全局變量

純函數(shù)便于函數(shù)的組成,有助于消除函數(shù)組合出來新的行為正確與否的擔(dān)憂:純函數(shù)的組合依舊是純函數(shù)。

不變性

js中的String類是最適合說不變性的。字符串類型的變量在調(diào)用方法后,返回調(diào)用后的值,但字符串本身是沒有被改變的。

鏈?zhǔn)秸{(diào)用

鏈?zhǔn)秸{(diào)用看underscore_.chain的源碼就好。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容