#### 前言
在計算機科學中,柯里化(英語:Currying),又譯爲卡瑞化或加里化,是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,而且返回接受餘下的參數並且返回結果的新函數的技術。javascript
#### 1、爲何會有函數柯里化?
Currying 的重要意義在於能夠把函數徹底變成「接受一個參數;返回一個值」的固定形式,這樣對於討論和優化會更加方便。 java
將關注的重點聚焦到函數自己,而不因冗餘的數據參數分散注意力。算法
有這樣的說法,並不是柯里化有什麼意義,而是,當函數能夠做爲函數的參數和返回值,成爲函數式編程語言後,就會不可避免地產生函數柯里化。編程
#### 2、具體實現
先來一個簡單的 add 函數瀏覽器
function add(x, y) { return x + y; } add(2, 3); // 5
重要的概念多說一遍:函數柯里化就是接收多個參數的函數變換爲接收一個函數,並返回接收餘下參數,最終能返回結果的技術
。閉包
那麼,繼續:app
function add(x) { return function(y) { return x + y; } } add(2)(3); // 5
因此,曾經的一個函數,由於閉包操做(返回函數並訪問了自由變量的行爲),變成了多個接收一個參數的函數。編程語言
因此簡單來說:函數柯里化就是意圖將函數的參數變成一個。讓函數能夠輸入一個值,就返回一個相對應的值,從而實現純函數化。函數式編程
爲何函數式編程要求函數必須是純的,不能有反作用?由於它是一種數學運算,原始目的就是求值,不作其餘事情,不然就沒法知足函數運算法則了。在函數式編程中,函數就是一個管道(pipe)。這頭進去一個值,那頭就會出來一個新的值,沒有其餘做用。函數
因此良好的編程規範是儘量讓函數塊作一個事情,實現可複用性,可維護性。
上面的例子中,若是有不少個參數怎麼辦,難道一層層嵌套?
咱們繼續:
function plus(value) { "use strict"; var add = function () { var args = []; var adder = function adder() { Array.prototype.push.apply(args,Array.prototype.slice.apply(arguments)) return adder; } adder.toString = function () { return args.reduce(function(a, b) { return a + b; }) } return adder; } return add()(value); } plus(2)(3)(5).toString(); // 10;
上面的代碼看起來不那麼優雅,若是是減法,咱們就得又從新爲減法寫這麼多的代碼。像 lodash, underscore 這些工具庫,都提供了柯里化的工具函數。
咱們一塊兒來試着實現:
function curry(fn, args) { var length = fn.length; // 函數參數的長度 // 閉包保存參數列表 args = args || []; return function() { // 獲取參數列表。 var _args = args.slice(0); Array.prototype.push.apply(_args, Array.prototype.slice.call(arguments)) if (_args.length < length) { // 若是傳入的參數列表長度尚未超過函數定義時的參數長度,就 push 新的參數到參數列表中保存起來。 // 本身調用本身,將保存的參數傳遞到下一個柯里化函數。 return curry.call(this, fn, _args); } else { // 若是傳入的參數列表長度已經超過函數定義時的參數長度,就執行。 return fn.apply(this, _args); } } }
函數柯里化的好處有幾個:
函數柯里化容許和鼓勵你分隔複雜功能變成更小更容易分析的部分。這些小的邏輯單元顯然是更容易理解和測試的,而後你的應用就會變成乾淨而整潔的組合,由一些小單元組成的組合。
文章開篇的 add 函數,假如,每次調用加法有一個初始值會怎樣?
var add = curry(function(a, b, c) { return a + b + c; }) var addTen = add(10); var addSix = add(6); addTen(2)(3); // 15; addSix(7)(8); // 21;
以上代碼就實現了參數複用,保存固定參數的函數。
看一個經典的例子:
元素綁定事件監聽器:
var addEvent = function(el, type, fn, capture) { if (window.addEventListener) { el.addEventListener(type, function(e) { fn.call(el, e); }, capture); } else if (window.attachEvent) { el.attachEvent("on" + type, function(e) { fn.call(el, e); }); } };
以上代碼是爲了兼容 IE 瀏覽器對 DOM 事件綁定作的函數封裝。
問題在於,每次對 DOM 元素進行事件綁定時,函數內部都會走一遍 if else。那麼用函數柯里化就能實現提早返回。
var addEvent = (function(){ if (window.addEventListener) { return function(el, sType, fn, capture) { el.addEventListener(sType, function(e) { fn.call(el, e); }, (capture)); }; } else if (window.attachEvent) { return function(el, sType, fn, capture) { el.attachEvent("on" + sType, function(e) { fn.call(el, e); }); }; } })();
函數柯里化是「函數是一等公民」的編程語言環境造成的編程風格,利用了函數能做爲參數一級返回值以及利用了閉包保存變量的特色,是將多個參數的函數轉換爲接收一個參數,最後返回結果的技術。
歡迎關注個人我的公衆號「謝南波」,專一分享原創文章。