持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天,点击查看活动详情
前言
函数柯里化,就是把函数的传参拆分成多次传参,每次接受参数之后,生成新的函数,该函数可以继续接受新的参数,或者返回当前函数执行后的结果。
具体例子
假设有这么一个求和函数:
/**
* 计算参数之和
* @param { number[] } arg 求和的数字组成的数组
* @returns { number }
*/
function add (...arg) {
return arg.reduce((all, num) => {
return isNaN(num) ? all : all + +num
}, 0)
}
如果存在以下求和情况:
add(1, 2, 3, 4, 5, 9, 12)
add(1, 2, 3, 7, 8)
add(1, 2, 3, 4, 6, 10, 11)
可以看到,在这几次调用中,add(1, 2, 3) 的情况都出现了,add(1, 2, 3, 4) 的情况出现了两次,那么能否先根据不同的参数,创建出不同的函数,方便后续的调用,不需要重复传入参数,如下:
// 先传入 1,2,3,生成自带这三个参数的 add 的柯里化函数
const curryingAdd_1 = currying(add, 1, 2, 3)
// 根据自带1,2,3参数的柯里化 add 函数,创建出自带 1,2,3,4 参数的柯里化 add 函数
const curryingAdd_2 = curryingAdd_1(4)
// 补充剩余的参数,求值
curryingAdd_2(5)(9)(12)()
curryingAdd_1(7, 8)()
curryingAdd_2(6)(10, 11)()
利用柯里化,可以在某些特定情况下,生成一些已经自带特定参数的函数,方便调用,简化代码,便于阅读。
代码
先定义柯里化函数 currying;该函数主要接受的参数分为两个部分,第一部分是需要被柯里化的函数 fn,第二部分是 fn 的部分参数 arg,arg 可以不传;返回值是 fn 被柯里化之后的函数 newFn;代码如下:
/**
* 函数柯里化
* @param { Function } fn 需要柯里化的函数
* @param { any[] } arg 其余参数
* @returns { Function }
*/
function currying (fn, ...arg) {
// 被柯里化之后的函数
const newFn = function () {}
// 返回被柯里化之后的函数
return newFn
}
在本文中,返回的被柯里化的函数 newFn 存在两种调用方法,一种是继续接受参数,返回新的被柯里化的函数;另一种是不传参,返回使用当前参数执行 fn 之后的返回值,具体代码如下:
function currying (fn, ...arg) {
// 检测 fn 是否为函数,如果不是,报错
if (typeof fn !== 'function') {
throw new Error (`${fn} is not a function !`)
}
// 被柯里化之后的函数
function newFn (...arg1) {
// 判断是否有传入参数:
// 如果没有传入参数,则根据当前参数执行 fn,并返回结果
// 如果有传入参数,则继续生成新的被柯里化的函数
return arg1.length === 0 ? fn(...arg, ...arg1) : currying(fn, ...arg, ...arg1)
}
// 返回被柯里化的函数
return newFn
}
测试
// 先传入 1,2,3,生成自带这三个参数的 add 的柯里化函数
const curryingAdd_1 = currying(add, 1, 2, 3)
// 根据自带1,2,3参数的柯里化 add 函数,创建出自带 1,2,3,4 参数的柯里化 add 函数
const curryingAdd_2 = curryingAdd_1(4)
// 补充剩余的参数,求值
curryingAdd_2(5)(9)(12)()
// 36
curryingAdd_1(7, 8)()
// 21
curryingAdd_2(6)(10, 11)()
// 37
总结
柯里化的难点在于参数的不确定,每次传参都可以生成一个新的被柯里化的函数;还有就是对于柯里化函数的取值方式有许多种,本文使用传参的数量进行判断是否取值。