第一次看到柯里化这个词的时候,是在群里听大佬们谈论一道面试题中了解到。那时一看这个词就感觉很懵逼,赶紧去了解了一下后才发现其实就是高阶函数的一个特殊用法。 题附上
实现一个函数,运算结果可以满足如下预期结果:
add(1)(2)(3) // 6
add(1, 2, 3) // 6
add(1)(2,3) // 6
add(1,2)(3) // 6
如果对柯里化有了解的同学,肯定一眼就看出答案了,主要是运用函数柯里化来实现。那么什么是柯里化呢?首先介绍一下什么是函数的柯里化(Currying)。《JavaScript忍者秘籍》一书中,对于柯里化的定义如下:
在一个函数中首先填充几个参数(然后再返回一个新函数)的技术称为柯里化(Currying。
现在让我们一步步来实现它
首先是 add(1)(2)(3) // 6
我们很快就能想到函数add
function add(x){
return function(y){
return function(z){
return x + y + z
}
}
}
接下来是 add(1, 2, 3) // 6
function add(x,y,z){
return x + y + z
}
那么问题来了 怎么把两个函数综合为一个函数呢? 通过题目我们可以发现,add参数的值的数量是相同的,只是传值的方式有所不同,所以本题的关键就是编写一个高阶函数 curry 让 add(1,2)(3) 同等转换为 add(1,2,3)
-
首先我们知道add函数的参数个数 为3,因此先编写函数addOrigin
function addOrigin(x,y,z){ return x + y + z }
-
编写函数curry,当参数数量达到addOrigin的个数时,返回addOrigin,否则继续递归下去
function curry(fn) { let arr = []; let depth = fn.length; const re = (...a) => { depth = depth - a.length; arr = [...arr, ...a] if (depth > 0) { // 若参数个数未达到fn的要求,进行递归操作 return re; } let result = [...arr]; arr = []; depth = fn.length; return fn(...result); }; return re; }
-
最后我们在通过curry这个高阶函数,生成我们需要的add函数
const add = curry(addOrigin)
-
最后 我们在控制太验证一下结果
大功告成!我们成功的利用curry 实现了函数的链式调用。同时也明白了 curry的核心思想——比较多次接受的参数总数与函数定义时的入参数量,当接受参数的数量大于或等于被 Currying函数的传入参数数量时,就返回计算结果,否则返回一个继续接受参数的函数。
上面代码中curry函数也可以简写为
function curry(fn, ...args) {
if (args.length >= fn.length) {
return fn(...args)
}
return function (...args2) {
return curry(fn, ...args, ...args2)
}
}
下面再看另一道很经典的面试题
实现一个函数,运算结果可以满足如下预期结果:
add(1)(2) // 3
add(1, 2, 3)(10) // 16
add(1)(2)(3)(4)(5) // 15
这道题跟上面一道题不一样的在于 参数的个数不固定,在网上找了一下答案 如下:
function add(...values) {
let sum = 0;
for (key of values) {
sum += key;
}
let tmp = (x) => {
sum += x;
return tmp;
};
tmp.toString = () => sum;
return tmp;
}
add(1)(2) // 3
add(1, 2, 3)(10) // 16
add(1)(2)(3)(4)(5) // 15
主要是利用重写对象的toString方法,但是问题在于 toString 不会主动执行, 后续有解决办法会再更新
下面是记录一下柯里化最常见的两个用途
- 参数复用: 固定不变的参数,实现参数复用是柯里化的主要用途之一
- 延迟执行: 延迟执行也是柯里化的一个重要使用场景,同样 bind 和箭头函数也能实现同样的功能。
总结
- 柯里化在 JavaScript 中是“低性能”的,但是这些性能在绝大多数场景,是可以忽略的。
- 柯里化的思想极大地助于提升函数的复用性。
- 柯里化生于函数式编程,也陷于函数式编程。假如没有准备好写纯正的函数式代码,那么 柯里化有更好的替代品。
- 函数式编程及其思想,是值得关注、学习和应用的事物。所以在文末再次安利JavaScript 程序员阅读此书 —— 《mostly-adequate-guide》