直白理解javascript中函数柯里化,偏函数,反函数柯里化
因为之前有写过一篇理解函数式编程的,但是讲到函数式编程就必须提到函数柯里化, 函数柯里化是函数式编程中很常见的写法,这篇也是看了很多篇大佬的文章综合理解,下面是个人的一写简洁,希望能够更直白的理解上面这些定义
函数柯里化
定义
官方定义: 是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术
简单说就是把一个一次性传入多个参数的函数转成每次传入一个参数返回一个另一个函数,例子:
add(1,2,3)
add(1)(2)(3)()
这就是函数柯里化的意思, 接下来先说怎么实现,再写柯里化的意义
实现柯里化
首先以add为例子,以add为例子在很多文档中也有基本都是一样的,这里我是用es6语法写,因为比较简单好理解
//普通写法
const add = (...values)=> {
return values.reduce((a,b)=> a+b)
}
//柯里化写法
const curringAdd = (...values)=> {
let _args = [...values] //第一次运行创建参数集合数组
const innerAdd = (...rest)=> {
_args = [..._args,...rest]//利用闭包缓存参数集合,并将之后参数不断加进去
if(rest.length === 0) { //判断未传入参数时直接返回结果,传入参数则返回新的函数
return _args.reduce((a,b)=> a+b)
}else {
return innerAdd
}
};
return innerAdd //第一次执行返回函数
}
//用柯里化函数调用方法
add(1)(2)(3)() //6
//也可以
add(1,2)(3)(4,5)() //15
这里已经实现了函数的柯里化,但是聪明的我们肯定能看出**,其实上面改写的柯里化的本质就是,始终将所有的参数放在一个数组中用闭包的形式缓存住,在最后一次调用中执行出结果**
既然这个逻辑是相同的我们当然可以写一个专门将普通函数转成柯里化函数的通用方法,具体代码如下
let toCurring = (fn)=>{
var _args = [];
const innerCurring = (...values)=>{
if(values.length === 0) {/判断未传入参数时直接返回结果,传入参数则返回新的函数
return fn.apply(this, _args);
}
_args = [..._args,...values] //利用闭包缓存参数集合,并将之后参数不断加进去
return innerCurring;
}
}
基本逻辑和我们之前写的柯里化的add是一样的,这样我们可以很简单的将普通函数柯里化
//普通函数
const add = (...values)=> {
return values.reduce((a,b)=> a+b)
}
//转成柯里化后函数
const curringAdd = toCurring(add)
下面很关键的,柯里化有什么意义
柯里化意义及作用
1. 参数复用(参数复用个人认为是偏函数的意义,后面会讲,虽然偏函数一般都是柯里化的写法);
2. 提前返回(提前返回很好理解,就是可以传入几个参数直接算出结果)
3.延迟计算/运行(延迟计算也好理解,就是每次传入参数不直接计算,最后才计算)
这里说一下个人对柯里化的作用的理解.
我们可以打个比方一个员工上班经常会外派出差,每次都要报销发票
普通函数的方法就是将所有发票攒在自己手里每隔一段时间去报销一次,每次都要拿着一大堆发票去财务那里报销,流程是: n * 存发票 => 取出n发票 => 报销
柯里化函数的方法就是每次只要讲发票交给财务,想用的时候直接和财务说一声就取出来了,你不需要自己攒着发票,流程就是: n * 交发票… => 报销
从这里看出柯里化后的函数流程很单一你不需要在函数外写一些与逻辑无关的存储数据的代码,你只要知道你只要每次到会计那里交发票就行了,我觉得最重要的是让我们代码更为逻辑清晰,让代码注重逻辑,而不用考虑怎么调用.
偏函数
定义
偏函数就是为了复用一些每次都传入的相同的参数,等于对原来的函数做一层封装. 有点类似于我们在调用别人封装好的组件时, 需要传入很多属性,但是有些属性在我们项目中时都是一样的,这时候就可以自己对组件再一次封装,将一些属性默认传入.
仿照上面的通用柯里化函数,我们可以写个通用的偏函数
const toPartial = (fn, ...argsFixed) {
return function(...args) { //
return fn.call(this, ...argsFixed, ...args); //将固定参数默认传入
}
}
//如果我们想让add函数默认传入2
const curringAdd = toCurring(add)
const partialAdd = toPartial(curringAdd,2)
partialAdd(1)() //3
偏函数在我们日常中是很常用的,我们通常对函数和组件的封装其实就是使用的偏函数的写法.
反柯里化
反柯里化顾名思义就是将每次传入一个参数返回一个函数的形式转回,一次性传入所有参数返回结果的形势\
简单实现:
const unCurring = (curringFn)=> {
return (...args)=> {
let lastFn = args.reduce(i=> curringFn(i))
return lastFn()
}
}
//将curringAdd转回普通函数
const add = unCurring(curringAdd)
add(1,2,3) //6
总结
柯里化函数就是把一个一次性传入多个参数的函数转成每次传入一个参数返回一个另一个函数
偏函数就是将原来的函数封装,默认转入固定参数复用参数
反柯里化函数就是柯里化的反向操作
柯里化函数是函数式编程的一种概念,不是必须的规范,而是在代码中常用的写法,作用就是让逻辑更清楚,让代码更注重业务流程.