什么是函数柯里化(柯里化:Currying)
函数柯里化是指实现一个函数,这个函数可以把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
一个简单的例子
// 普通的add函数
function add(x, y) {
return x + y
}
// Currying后
function curryingAdd(x) {
return function (y) {
return x + y
}
}
add(1, 2) // 3
curryingAdd(1)(2) // 3
柯里化函数的运行过程其实是一个参数的收集过程,我们将每一次传入的参数收集起来,并在最里层里面处理
函数柯里化的好处
1.参数复用
参数复用原理是利用闭包让前面传进来的参数不要被释放掉;
// 正常封装check函数进行字符串正则匹配
function check(reg, txt) {
return reg.test(txt)
}
check(/\d+/g, 'test') //false
check(/[a-z]+/g, 'test') //true
// 使用柯里化函数进行字符串正则匹配
function curryingCheck(reg) {
return function(txt) {
return reg.test(txt)
}
}
let hasNumber = curryingCheck(/\d+/g)
let hasLetter = curryingCheck(/[a-z]+/g)
hasNumber('test1') // true
hasNumber('testtest') // false
hasLetter('21212') // false
2.提前确认
这一特性经常是用来对浏览器的兼容性做出一些判断并初始化api,比如说我们目前用来监听事件大部分情况是使用addEventListener来实现的,但是一些较久的浏览器并不支持该方法,所以在使用之前,我们可以先做一次判断,之后便可以省略这个步骤了。
let on = function(isSupport, element, event, handler) {
isSupport = isSupport || document.addEventListener;
if (isSupport) {
return element.addEventListener(event, handler, false);
} else {
return element.attachEvent('on' + event, handler);
}
}
3. 延迟运行
js中的bind这个方法,用到的就是柯里化的这个特征。
Function.prototype.bind = function (context) {
var _this = this
var args = Array.prototype.slice.call(arguments, 1)
return function() {
return _this.apply(context, args)
}
}
封装一个通用的 多参数的柯里化函数
// 支持多参数传递
function progressCurrying(fn, args) {
let _this = this
let len = fn.length;
let args = args || [];
return function() {
let _args = Array.prototype.slice.call(arguments);
Array.prototype.push.apply(args, _args);
// 如果参数个数小于最初的fn.length,则递归调用,继续收集参数
if (_args.length < len) {
return progressCurrying.call(_this, fn, _args);
}
// 参数收集完毕,则执行fn
return fn.apply(this, _args);
}
}
一道经典面试题
// 实现一个add方法,使计算结果能够满足如下预期:
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;
function add() {
// 第一次执行时,定义一个数组专门用来存储所有的参数
let _args = Array.prototype.slice.call(arguments);
// 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
let _adder = function() {
_args.push(...arguments);
return _adder;
};
// 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
_adder.toString = function () {
return _args.reduce(function (a, b) {
return a + b;
});
}
return _adder;
}
add(1)(2)(3) // 6
add(1, 2, 3)(4) // 10
add(1)(2)(3)(4)(5) // 15
add(2, 6)(1) // 9
// 其实上例子中的add方法,就是下面这个函数的柯里化函数,只不过我们并没有使用通用式来转化,而是自己封装
function add(...args) {
return args.reduce((a, b) => a + b);
}