一、什么是柯里化?
柯里化(Currying)是一种关于函数的高阶技术。它不仅被用于 JavaScript,还被用于其他编程语言。柯里化是一种函数的转换,它是指将一个函数从可调用的 f(a, b, c) 转换为可调用的 f(a)(b)(c)。柯里化不会调用函数。它只是对函数进行转换。
维基百科版定义:
是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
简单版定义:
只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的函数。
二、柯里化有什么用?
举个例子。
var add = function (x) {
return function (y) {
return x + y;
};
};
var increment = add(1);
var addTen = add(10);
console.log(increment(2)); // 3
console.log(addTen(2)); // 12
这表明curry是一种“预加载”函数的能力,通过传递一到两个参数调用函数,就能得到一个记住了这些参数的新函数。
再举个例子:有一个用于格式化和输出日志的函数log(date, level, msg)。在实际项目中,此函数具有很多有用的功能,例如通过网络发送日志等。
function log(date, level, msg) {
console.log(`[${date.getHours()}:${date.getMinutes()}] [${level}] ${msg}`);
}
将它柯里化:
var log = _.curry(log); // 使用 lodash 的 curry 方法
柯里化之后,log仍然可以正常运行:
log(new Date(), 'DEBUG', 'some debug'); // log(a, b, c)
同时,也可以以柯里化的形式调用:
log(new Date())('DEBUG')('some debug'); // log(a)(b)(c)
现在,我们可以轻松地为当前日志创建便捷函数:
// logNow 是一个带有第一个固定参数的偏函数(partial)
logNow = log(new Date());
// 使用它
logNow('INFO', 'some info msg');
// 进而,也可以创建一个为 当前时间 DEBUG 级别的 便捷方法
debugNow = logNow('DEBUG');
// 使用它, 只需要一个参数
debugNow('only msg here');
所以:
-
柯里化之后,我们没有丢失任何东西,如log依然可以正常被调用
-
此外还可以轻松地生成偏函数(
partial function)
三、实现curry函数
提问:请实现一个函数,实现以下功能:
add(1, 2, 3); // 6
add(1,2)(3); // 6
add(1)(2)(3); // 6
答案:
function curry(func) {
return function curried(...args) {
if (args.length >= func.length) {
return func.apply(this, args);
} else {
return function (...args2) {
return curried.apply(this, args.concat(args2));
};
}
};
}
// 另一种实现
function curry(fn) {
return (...args) => {
if (args.length >= fn.length) {
return fn.apply(null, args);
} else {
return curry(fn.bind(null, ...args));
}
};
}
测试一下:
function sum(a, b, c) {
return a + b + c;
}
var curriedSum = curry(sum);
console.log(curriedSum(1, 2, 3)); // 6,正常形式调用
console.log(curriedSum(1, 2)(3)); // 6,以偏函数的方式调用
console.log(curriedSum(1)(2)(3)); // 6,以偏函数的方式调用