前言
柯里化(Currying): 是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术.
柯里化是很多高级编程语言拥有的特性,比如:JS、scala。
柯里化的好处:
1、参数复用。
2、提前返回。
3、 延迟计算/运行。
什么是柯里化
函数柯里化(currying)又称部分求值。一个 currying 的函数首先会接受一些参数,接受了这些参数之后,该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值。
面试的时候: 是不是经常遇到面试题,要求实现:
sum(1,2,3);// 6
sum(1,2)(3);// 6
sum(1)(2,3);// 6
这类面试题,这个实现后面会详解
先举两个例子:
function add(num1, num2){
return num1+num2;
}
function curriedAdd(num2) {
return add(5, num2);
}
console.log(add(2,3));//5
console.log(curriedAdd(3));
从技术上来说 curriedAdd 并不是柯里化的函数,但是很好展示其概念
2.实现函数只执行一次
function onesFtn(fun) {
let flag = true;
return function() {
if (flag) {
fun && fun();
flag = false;
}
}
}
let onceConsole = onesFtn(()=> {
console.log('只执行一次');
});
onceConsole();
onceConsole();
这个是不是很熟悉,闭包不就是这个吗,对了,柯里化就是以闭包为基础。
柯里化通用实现:(javaScript高级程序设计 第三版 604页)
function curry (fn) {
var args =Array.prototype.slice.call(arguments, 1);// 获取所有参数(除第一个外)的数组
return function (){
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.cocat(innerArgs);
return fn.apply(null,finalArgs);// 这个函数并没有考虑到执行环境,所以apply第一个参数是null,可以对比bind函数实现来看
}
}
第一个例子 可以改成
function add(num1,num2) {
return n1+n2;
}
var cur =curry(add,5);
console.log(cur,3);
这样是不是就明白了
同学们有没有发现这个函数很有局限性,参数不够自由,只能传2个
如:
var addFtn = curry(function(a,b,c){
return a+b+c;
})
addFtn(2)(3);////TypeError: addFun(...) is not a function
改进如下:es6写法
function curry (func) {
const curriedFn = (...args) => {
// 判断实参、形参个数
if (args.length < func.length) {
return (...args1) => {
// 这个func用来获取arguments
//下面三种写法都可以
// return curriedFn(...args.concat(Array.from(args1)));
// return curriedFn(...args.concat(...args1));
return curriedFn(...args.concat(args1));
}
}
return func(...args);
}
return curriedFn;
}
const getSum = (a, b, c) => {
return a + b + c;
};
const curried = curry(getSum);
console.log(curried(1, 2, 3));
console.log(curried(1,2)(3));
console.log(curried(1)(2, 3));
console.log(curried(1)(2)(3));
再精简一下:
const curry = (func) => {
return curriedFn = (...args) => {
// 判断实参、形参个数
if (args.length < func.length) {
return (...args1) => {
// 这个func用来获取arguments
//下面三种写法都可以
// return curriedFn(...args.concat(Array.from(args1)));
// return curriedFn(...args.concat(...args1));
return curriedFn(...args.concat(args1));
}
}
return func(...args);
}
}
const getSum = (a, b, c) => {
return a + b + c;
};
const curried = curry(getSum);
console.log(curried(1, 2, 3));
console.log(curried(1,2)(3));
console.log(curried(1)(2, 3));
console.log(curried(1)(2)(3));
讲到这,干脆再写一篇函数绑定bind实现