JavaScript 專題系列第十四篇,講解偏函數以及如何實現一個 partial 函數git
維基百科中對偏函數 (Partial application) 的定義爲:github
In computer science, partial application (or partial function application) refers to the process of fixing a number of arguments to a function, producing another function of smaller arity.app
翻譯成中文:函數
在計算機科學中,局部應用是指固定一個函數的一些參數,而後產生另外一個更小元的函數。this
什麼是元?元是指函數參數的個數,好比一個帶有兩個參數的函數被稱爲二元函數。翻譯
舉個簡單的例子:code
function add(a, b) { return a + b; } // 執行 add 函數,一次傳入兩個參數便可 add(1, 2) // 3 // 假設有一個 partial 函數能夠作到局部應用 var addOne = partial(add, 1); addOne(2) // 3
我的以爲翻譯成「局部應用」或許更貼切些,如下所有使用「局部應用」。排序
若是看過上一篇文章《JavaScript專題之柯里化》,實際上你會發現這個例子和柯里化太像了,因此二者究竟是有什麼區別呢?遞歸
其實也很明顯:ip
柯里化是將一個多參數函數轉換成多個單參數函數,也就是將一個 n 元函數轉換成 n 個一元函數。
局部應用則是固定一個函數的一個或者多個參數,也就是將一個 n 元函數轉換成一個 n - x 元函數。
若是說二者有什麼關係的話,引用 functional-programming-jargon 中的描述就是:
Curried functions are automatically partially applied.
咱們今天的目的是模仿 underscore 寫一個 partial 函數,比起 curry 函數,這個顯然簡單了不少。
也許你在想咱們能夠直接使用 bind 吶,舉個例子:
function add(a, b) { return a + b; } var addOne = add.bind(null, 1); addOne(2) // 3
然而使用 bind 咱們仍是改變了 this 指向,咱們要寫一個不改變 this 指向的方法。
根據以前的表述,咱們能夠嘗試着寫出初版:
// 初版 // 似曾相識的代碼 function partial(fn) { var args = [].slice.call(arguments, 1); return function() { var newArgs = args.concat([].slice.call(arguments)); return fn.apply(this, newArgs); }; };
咱們來寫個 demo 驗證下 this 的指向:
function add(a, b) { return a + b + this.value; } // var addOne = add.bind(null, 1); var addOne = partial(add, 1); var value = 1; var obj = { value: 2, addOne: addOne } obj.addOne(2); // ??? // 使用 bind 時,結果爲 4 // 使用 partial 時,結果爲 5
然而正如 curry 函數可使用佔位符同樣,咱們但願 partial 函數也能夠實現這個功能,咱們再來寫第二版:
// 第二版 var _ = {}; function partial(fn) { var args = [].slice.call(arguments, 1); return function() { var position = 0, len = args.length; for(var i = 0; i < len; i++) { args[i] = args[i] === _ ? arguments[position++] : args[i] } while(position < arguments.length) args.push(argumetns[position++]); return fn.apply(this, args); }; };
咱們驗證一下:
var subtract = function(a, b) { return b - a; }; subFrom20 = partial(subtract, _, 20); subFrom20(5);
值得注意的是:underscore 和 lodash 都提供了 partial 函數,但只有 lodash 提供了 curry 函數。
JavaScript專題系列目錄地址:https://github.com/mqyqingfeng/Blog。
JavaScript專題系列預計寫二十篇左右,主要研究平常開發中一些功能點的實現,好比防抖、節流、去重、類型判斷、拷貝、最值、扁平、柯里、遞歸、亂序、排序等,特色是研(chao)究(xi) underscore 和 jQuery 的實現方式。
若是有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。若是喜歡或者有所啓發,歡迎 star,對做者也是一種鼓勵。