Curry函数
最近面试提到柯里化(Currying),虽然看了一些柯里化的文章,但从没有深入做出一些总结。所以现在开始写这一篇文章,来为它做出一些总结。
什么是函数柯里化
柯里化通常也称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果。
因此柯里化的过程是逐步传参,逐步缩小函数的适用范围,逐步求解的过程。
柯里化函数的作用
函数柯里化允许和鼓励你分隔复杂功能变成更小更容易分析的部分。这些小的逻辑单元显然是更容易理解和测试的,然后你的应用就会变成干净而整洁的组合,由一些小单元组成的组合。
柯里化的实现依赖
- 函数可以作为参数传递
- 函数能够作为函数的返回值
- 闭包
函数柯里化的特点
柯里化(Currying)具有的特点:
- 延迟计算:上面的例子已经比较好的说明了。
- 参数复用:当在多次调用同一个函数,并且传递的参数绝大多数是相同的,那么该函数可能是一个很好的柯里化候选。
- 动态创建函数:这可以是在部分计算出结果后,在此基础上动态生成新的函数处理后面的业务,这样省略了重复计算。或者可以通过将要传入调用函数的参数子集,部分应用到函数中,从而动态创造出一个新函数,这个新函数保存了重复传入的参数(以后不必每次都传)。例如,事件浏览器添加事件的辅助方法。
关于性能:
- 如果内部使用arguments,存取arguments对象通常要比存取命名参数要慢一点.
- 一些老版本的浏览器在arguments.length的实现上是相当慢的.
- 创建大量嵌套作用域和闭包函数会带来花销,无论是在内存还是速度上.
函数柯里化案例
基础的Curry
function add(a, b) {
// 局部应用
if (typeof a === 'undefined') {
return add
}
else if (typeof b === 'undefined') {
return function (newB) {
return a + newB // 此处a的值因闭包而保存
}
}
// 完全应用
return a + b
}
add() // add函数
var newAdd = add(1) // 局部应用了参数1的函数
newAdd(3) // 4
add(1, 4) // 5 完全应用
通用化的Curry
function currying(fn, ..._args) {
return function (args) {
return fn.apply(null, _args.concat(...args))
}
}
const newAdd = currying(function (a, b) {
return a + b
}, 1)
console.log(newAdd(5)) // 6
增强通用化的Curry
- 通过JS对原始数据的转换规则
function currying(func) {
/**
* 闭包原理
* 通过返回函数索引闭包内变量
* 将_args保存在闭包(_adder)内,不被回收机制回收,从而反复收集参数.
* */
let _args = []
return function adder(...args) {
// 无新参数返回旧值,否则计算新值
if (args.length) {
// 将所有新参数添加至私有变量中
_args.push(...args)
// valueOf 权限大于 toString,它能将数值正确反映到函数上
adder.valueOf = adder.toString = () => func(_args)
}
return adder
}
}
// 将所有函数调用的值都追加到 currying 函数的一个闭包变量中,最后统一处理
const add = currying(arr => arr.reduce((a, b) => a + b))
console.log(add(1, 2)(2)(5, 5)(5) + 1) // 21
当一个对象转换成原始值时,先查看对象是否有valueOf方法。
如果没有valueOf 或 返回的不是原始值,那么调用toString方法,返回字符串表示。valueOf 它能覆盖 toString 的数值.
- 当没有参数时,一次性返回计算结果
function currying(func) {
const _args = []
return function adder(...args) {
_args.push(...args)
if (args.length === 0) {
return func(_args)
}
return adder
}
}
const add = currying(arr => arr.reduce((a, b) => a + b))
console.log(add(1, 2)(3)(1, 1, 5)(5)()) // 18