柯里化: 是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
面试中嘴jian,提了一下函数柯里化思想,面试官反问我函数柯里化有哪些应用场景,回答了只知道bind的内部实现,还好面试官说在实际工作中也不常用,问题不大。不过名字叫得这么高大上,还是了解一下比较好。
概念过于抽象,看下面例子马上理解:
// 普通的add函数
function add(x, y) {
return x + y
}
// 柯里化后
function curryingAdd(x) {
return function (y) {
return x + y
}
}
add(1, 2) // 3
curryingAdd(1)(2) // 3
bind(不会立即执行)也是经典的柯里化例子,看看bind的内部实现:
//bind内部实现
Function.prototype.bind = function(context) {
let cxt = JSON.parse(JSON.stringify(connext)) || window;
cxt.func = this;
//获取实参
let args = Array.from(arguments).slice(1);
//返回函数
return function() { //通过闭包预存第一次传的参数
let allArgs = args.concat(Array.from(arguments));
return allArgs.length > 0 ? cxt.func(...allArgs) : cxt.func();
}
}
(闭包是函数柯里化的灵魂,有信心的话回答闭包的应用时可以顺便一提,然后…)
// 支持多参数传递
function progressCurrying(fn, args) {
var _this = this
var len = fn.length;
var args = args || [];
return function() {
var _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)(4)(5) = 15;
add(1, 2, 3)(4)(5) = 15;
add(1)(2)(3)(4)(5) = 15;
function currying(fn, length) {
length = length || fn.length; //fn.length:被柯里化的函数形参的个数
return function(...args) {
if (args.length >= length) { //判断参数是否收集完毕
return fn(...args); // 参数收集完毕,则执行fn
} else {
return currying(fn.bind(null, ...args), length - args.length);
} // 参数未收集完毕,则递归调用fn,继续收集参数
}
}
let add = currying((...arg) => eval(arg.join('+')), 5);
let a = add(1)(2)(3)(4)(5)
let b = add(1, 2, 3)(4)(5)
let c = add(1)(2)(3)(4)(5)
console.log(a); //15
console.log(b); //15
console.log(c); //15
eval( ) :将字符串转为可执行的js代码
参考文章:详解JS函数柯里化