前端開發者進階之函數柯里化Currying
穆乙:http://www.rzrgm.cn/pigtail/p/3447660.html
在計算機科學中,柯里化(英語:Currying),又譯為卡瑞化或加里化,是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,并且返回接受余下的參數而且返回結果的新函數的技術。這個技術由 Christopher Strachey 以邏輯學家哈斯凱爾·加里命名的,盡管它是 Moses Sch?nfinkel 和 Gottlob Frege 發明的。
這是來自維基百科的名詞解釋。顧名思義,柯里化其實本身是固定一個可以預期的參數,并返回一個特定的函數,處理批特定的需求。這增加了函數的適用性,但同時也降低了函數的適用范圍。
看一下通用實現:
function currying(fn) { var slice = Array.prototype.slice, __args = slice.call(arguments, 1); return function () { var __inargs = slice.call(arguments); return fn.apply(null, __args.concat(__inargs)); }; }
柯里化的實用性體現在很多方面:
1 提高適用性。
【通用函數】解決了兼容性問題,但同時也會再來,使用的不便利性,不同的應用場景往,要傳遞很多參數,以達到解決特定問題的目的。有時候應用中,同一種規則可能會反復使用,這就可能會造成代碼的重復性。
看下面一個例子:
function square(i) { return i * i; } function dubble(i) { return i *= 2; } function map(handeler, list) { return list.map(handeler); } // 數組的每一項平方 map(square, [1, 2, 3, 4, 5]); map(square, [6, 7, 8, 9, 10]); map(square, [10, 20, 30, 40, 50]); // ...... // 數組的每一項加倍 map(dubble, [1, 2, 3, 4, 5]); map(dubble, [6, 7, 8, 9, 10]); map(dubble, [10, 20, 30, 40, 50]);
例子中,創建了一個map通用函數,用于適應不同的應用場景。顯然,通用性不用懷疑。同時,例子中重復傳入了相同的處理函數:square和dubble。
應用中這種可能會更多。當然,通用性的增強必然帶來適用性的減弱。但是,我們依然可以在中間找到一種平衡。
看下面,我們利用柯里化改造一下:
function square(i) { return i * i; } function dubble(i) { return i *= 2; } function map(handeler, list) { return list.map(handeler); } var mapSQ = currying(map, square); mapSQ([1, 2, 3, 4, 5]); mapSQ([6, 7, 8, 9, 10]); mapSQ([10, 20, 30, 40, 50]); // ...... var mapDB = currying(map, dubble); mapDB([1, 2, 3, 4, 5]); mapDB([6, 7, 8, 9, 10]); mapDB([10, 20, 30, 40, 50]); // ......
我們縮小了函數的適用范圍,但同時提高函數的適性。當然,也有擴展函數適用范圍的方法--反柯里化,留到下一篇再討論。
由此,可知柯里化不僅僅是提高了代碼的合理性,更重的它突出一種思想---降低適用范圍,提高適用性。
下面再看一個例子,一個應用范圍更廣泛更熟悉的例子:
function Ajax() { this.xhr = new XMLHttpRequest(); } Ajax.prototype.open = function(type, url, data, callback) { this.onload = function() { callback(this.xhr.responseText, this.xhr.status, this.xhr); } this.xhr.open(type, url, data.async); this.xhr.send(data.paras); } 'get post'.split(' ').forEach(function(mt) { Ajax.prototype[mt] = currying(Ajax.prototype.open, mt); }); var xhr = new Ajax(); xhr.get('/articles/list.php', {}, function(datas) { // done(datas) }); var xhr1 = new Ajax(); xhr1.post('/articles/add.php', {}, function(datas) { // done(datas) });
2 延遲執行。
柯里化的另一個應用場景是延遲執行。不斷的柯里化,累積傳入的參數,最后執行。
看下面:
var add = function() { var _this = this, _args = arguments return function() { if (!arguments.length) { var sum = 0; for (var i = 0, c; c = _args[i++];) sum += c return sum } else { Array.prototype.push.apply(_args, arguments) return arguments.callee } } } add(1)(2)(3)(4)();//10
通用的寫法:
var curry = function(fn) { var _args = [] return function cb() { if (arguments.length == 0) { return fn.apply(this, _args) } Array.prototype.push.apply(_args, arguments); return cb; } }
上面累加的例子,可以實驗一下怎么寫?
3 固定易變因素。
柯里化特性決定了它這應用場景。提前把易變因素,傳參固定下來,生成一個更明確的應用函數。最典型的代表應用,是bind函數用以固定this這個易變對象。
Function.prototype.bind = function(context) { var _this = this, _args = Array.prototype.slice.call(arguments, 1); return function() { return _this.apply(context, _args.concat(Array.prototype.slice.call(arguments))) } }
參考:
http://sombie.diandian.com/post/2013-06-28/40050585369
http://book.2cto.com/201211/9320.html
http://zh.wikipedia.org/wiki/Currying
http://www.ibm.com/developerworks/cn/web/1006_qiujt_jsfunctional/

浙公網安備 33010602011771號