柯里化的定义
引自维基百科
在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数)的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
例子
function(a b) {return a + b;}// 正常执行add(1, 2) // 3// 假设有个 curry 的函数const addCurry = curry(add);// 柯里化后执行方式addCurry(1,2) // 3addCurry(1)(2) // 3
用途
好了我们已经知道了什么是柯里化了,但柯里化到底有什么用了?
例子
// 一个简答的 ajax 请求function ajax(type, url, data) {const xhr = new XMLHttpRequest();xhr.open(type, url, true);xhr.send(data);}// 每次发送一个 ajax 请求都需要写上一系列的参数,显然这样的非常大麻烦。ajax("POST", "www.example.com", "name=cxx");ajax("GET", "www.example.com", "name=cxx");// 利用 curryconst ajaxCurry = curry(ajax);// 以 POST 类型请求数据const post = ajaxCurry("POST");post("www.example.com", "name=cxx");// 以 POST 类型请求来自于 www.example.com 的数据const postFromTest = post("www.example.com");postFromTest("name=cxx");
现在好了,我们知道了 curry 的作用。
- 参数复用。
 - 延迟计算/运行。
 
实现 curry 函数(第一版)
const curry = function (fn) {const args = [].slice.call(arguments, 1);return function () {const newArgs = args.concat([].slice.call(arguments));return fn.apply(this, newArgs);};};
const addCurry1 = curry(add, 1, 2);addCurry1(); // 3//或者const addCurry2 = curry(add, 1);addCurry2(2); // 3//或者const addCurry3 = curry(add);addCurry3(1, 2); // 3
已经有柯里化的感觉了,但还是没有达到要求。不过可以借助这个函数来实现真正的 curry 函数。
实现 curry 函数(第二版)
// 第二版function sub_curry(fn) {const args = [].slice.call(arguments, 1);return function () {return fn.apply(this, args.concat([].slice.call(arguments)));};}function curry(fn, length) {length = length || fn.length;const slice = Array.prototype.slice;return function () {if (arguments.length < length) {const combined = [fn].concat(slice.call(arguments));return curry(sub_curry.apply(this, combined), length - arguments.length);} else {return fn.apply(this, arguments);}};}
const fn = curry(function (a, b, c) {return [a, b, c];});// 以下输出都是 ['a', 'b', 'c']console.log(fn("a", "b", "c"));console.log(fn("a", "b")("c"));console.log(fn("a")("b")("c"));console.log(fn("a")("b", "c"));
这个代码有点难理解,举一个简单的例子方便理解。
function sub_curry(fn) {return function () {return fn();};}function curry(fn, length) {length = length || 4;return function () {if (length > 1) {return curry(sub_curry(fn), --length);} else {return fn();}};}const fn0 = function () {console.log(1);};const fn1 = curry(fn0);console.log(fn1()()()()); // 1
先从理解 curry 函数开始。
当执行 fn1() 时,函数返回:
// fn0 看作 Acurry(sub_curry(A))// 相当于curry(function(){return A()})
当执行 fn1()() 时,函数返回:
curry(sub_curry(function(){return A()}))// 相当于curry(function(){return (function(){return A()})()})// 相当于curry(function(){return A()})
当执行 fn1()()() 时,函数返回:
// 跟 fn1()() 的分析过程一样curry(function(){return A()})
当执行 fn1()()()() 时,因为此时的 length > 2 为 false,所以执行 fn():
fn()// 相当于(function(){return A()})()// 相当于A()// 执行 A(fn0) 函数,打印 1
更易懂的实现
function curry(fn, args) {const length = fn.length;args = args || [];return function () {let _args = args.slice(0), arg, i;for (i = 0; i < arguments.length; i++) {arg = arguments[i];_args.push(arg);}if (_args.length < length) {return curry.call(this, fn, _args);} else {return fn.apply(this, _args);}};}const fn = curry(function (a, b, c) {console.log([a, b, c]);});fn("a", "b", "c"); // ["a", "b", "c"]fn("a", "b")("c"); // ["a", "b", "c"]fn("a")("b")("c"); // ["a", "b", "c"]fn("a")("b", "c"); // ["a", "b", "c"]
参考:
[1] JavaScript专题之函数柯里化
[2] 柯里化
