什么是函数柯里化
const addPrefix = (prefix, arr) => arr.map(item => prefix + item)
const newAPI = addPrefix('/api', ['/getName', '/getAge', '/getAddress'])
console.log(newAPI)
// 结果:['/api/getName', '/api/getAge', '/api/getAddress']
上述函数用于给数组的每一项增加统一前缀
当该函数调用次数较少时没有问题
但是如果该函数需要在多处被调用多次,且需传入相同前缀时
则会导致出现大量重复代码,且耦合程度变大,例如:
const api1 = addPrefix('/api', [...])
// ...
const api2 = addPrefix('/api', [...])
// ...
const api3 = addPrefix('/api', [...])
// ...
const api4 = addPrefix('/api', [...])
如果需要变更需求,调整 /api
为 /newAPI
,则需要一个一个修改或全文件替换
此时想到,用一个变量来代替所有需要用到 /api
的地方:
const API = '/api'
const api5 = addPrefix(API, [...])
这样后续只需要修改变量的赋值即可
这样可以化解代码耦合程度的问题,但是并没有解决重复代码的问题
并且我们想到,如果此时这个页面还可能需要添加其它的前缀呢
例如有 5 处需要调用函数并添加 /api
这个前缀,但是在另外 6 处需要调用函数添加 /person
这个前缀
这个时候就需要定义两个变量,然后调用十次 addPrefix
,并且每一次都要分别传入相应变量
这样做,在后续维护时,由于都是调用 addPrefix
,导致逻辑的区分根据传入的变量不同来辨别
而我们希望一个函数只做一件事,且一眼就能看出他是做什么事的
因此可以想到,将 addPrefix
二次封装得到 addApi
和 addPerson
这样在需要给某处的数组加前缀的时候,我们只需要关心调用哪一个函数
于是得到:
const addApi = arr => addPrefix('/api', arr)
const addPerson = arr => addPrefix('/api', arr)
const api1 = addApi([...])
const api2 = addApi([...])
const person1 = addPerson([...])
const person2 = addPerson([...])
经过上述的封装,后续维护就只需要关注 addApi
和 addPerson
这两个函数
但是又有新的问题出现了,如果需要封装多个这种函数,就需要重新写很多次这个封装过程
因此我们将这个封装的过程也封装到函数内部,重新定义 addPrefix
:
const addPrefix = prefix => arr => arr.map(item => prefix + item)
重写后的 addPrefix
接收一个 prefix
作为参数,并返回一个新的函数
该函数接收一个数组,并将之前传入的 prefix
作为前缀,拼接到传入数组的每一项
因此之前的代码又可以改写为:
// 封装过程被封装
const addApi = addPrefix('/api')
const addPerson = addPrefix('/person')
// 调用还是一样
const api1 = addApi([...])
const api2 = addApi([...])
const person1 = addPerson([...])
const person2 = addPerson([...])
以上过程就是一个最简单的函数柯里化
对任意函数进行柯里化
以上函数只需要两个参数,如果是一个需要三个甚至四个参数的函数,那该如果柯里化呢,其实也是一样的:
例如我们既需要加一个前缀还需要加一个后缀,那我们需要将 addPrefix
再次重写:
const addPrefix = prefix => {
// 方便阅读,这里不简写箭头函数的写法
return suffix => {
return arr => arr.map(item => prefix + item + suffix)
}
}
const addAB = addPrefix('a')('b')
const api = addAB(['1', '2', '3'])
console.log(api)
// ['a1b', 'a2b', 'a3b']
会发现返回一个函数的这个过程越嵌越深,因此我们需要实现一个可以将任意函数柯里化的函数:
const curry = fn => {
return function recur (...arg) {
if (arg.length < fn.length) {
// 已传入的参数个数,小于函数需要的参数个数
return (...restArg) => recur(...arg, ...restArg)
} else {
// 传入的参数足够,调用函数
return fn(arg)
}
}
}
通过这个函数,就可以传入的函数柯里化,达到之前想要的效果:
const addPrefix = (prefix, suffix, arr) => arr.map(item => prefix + item + suffix)
const addAB = curry(addPrefix)('a')('b')
const result = addAB(['1', '2', '3'])
const result2 = addAB(['2', '3', '4'])
console.log(result)
console.log(result2)
// ['a1b', 'a2b', 'a3b']
// ['a2b', 'a3b', 'a4b']