柯里化算是特殊的偏函数,把一个多参数函数转换成多个单参数函数,也就是说把一个具备n个参数的函数转换成n个一元函数
// 正常写法 function add (a, b) { return a + b } const resAdd = add(2, 3) console.log(resAdd) // 5 // 柯里化 function currieAdd (a) { return function (b) { return a + b } } const resCurrie = currieAdd(2)(3) console.log(resCurrie) // 5
上面的示例代码比较简单,若是有十几个参数呢?因此须要一个通用柯里化的写发
代码的关键点在于javascript
- 闭包,调用柯里化函数(currie)返回另一个函数(_myFn),经过闭包缓存真正执行运算的函数(fn)和参数(args)
- 经过返回的函数传递参数,并进行判断,若是参数已经传递够了,就执行函数(fn)并返回结果,若是参数还没传递完,则继续返回函数(_myFn)接收参数
// 柯里化一个函数 function currie (fn) { // 利用闭包缓存传递进来的参数 const args = [] return function _myFn (arg) { args.push(arg) if (args.length === fn.length) { // 说明参数已经传递够了,执行fn函数并返回结果 return fn.apply(null, args) } else { // 发现参数没有传递完,则返回_myfn函数,继续调用 return _myFn } } } // 示例 1 function add_1 (a, b) { return a + b } const currieAdd_1 = currie(add_1) const res1 = currieAdd_1(2)(3) console.log(res1) // 5 // 示例 2 function add_2 (a, b, c, d, e) { return a + b + c + d + e } const currieAdd_2 = currie(add_2) const res2 = currieAdd_2(1)(2)(3)(4)(5) console.log(res2) // 15
偏函数又叫局部应用,固定函数的一个或多个参数,也就是说把一个n
元函数转换成一个n - x
元函数
// 封装一个ajax方法 function ajax (url, data, callback) { ... } // 调用ajax方法, ajax('http://lyn.com', { a: 'aa' }, function () { // 回调 A }) ajax('http://lyn.com', { b: 'bb' }, function () { // 回调 B }) ... ajax('http://lyn.com', { y: 'yy' }, function () { // 回调 Y })
发现以上全部的调用,第一个参数都同样,这时候就须要想有没有什么方法能够简化这种重复参数的填写,
偏函数
出马
// 偏函数 function partial (url) { return function (data, cb) { ajax(url, data, cb) } } // 调用偏函数 const partialAjax = partial('http://lyn.com') // 发送ajax请求 partialAjax({ a: 'aa' }, function () { // 回调 A }) partialAjax({ b: 'bb' }, function () { // 回调 B }) ... partialAjax({ y: 'yy' }, function () { // 回调 Y })
代码的关键点java
偏函数的代码比较简单,就是利用闭包缓存实际的执行方法(fn)和与之的参数(preset),而后返回一个接收剩余参数的方法,方法的实现就是执行fn并返回结果
function partial (fn, ...preset) { return function (...args) { return fn.apply(null, preset.concat(args)) } } // 示例, 经过一个简单的add方法来模拟 function add (a, b, c, d) { return a + b + c + d } // 屡次调用传递的前两个参数是同样的 // add(1, 2, 3, 4) // add(1, 2, 5, 6) const partialAdd = partial(add, 1, 2) const res1 = partialAdd(3, 4) console.log(res1) // 10 const res2 = partialAdd(5, 6) console.log(res2) // 14
柯里化其实就是偏函数的特殊状况,因此在反柯里化这里就之说偏函数,我以为这样更合适
- 偏函数:偏函数是对高阶函数的降阶处理,再朴素点的描述就是,下降函数的通用性,建立一个针对性更强的函数,好比上面讲的
偏函数
部分的ajax
和partialAjax
- 反柯里化:和偏函数恰好相反,增长方法的适用范围(即通用性)
Function.prototype.uncurrie = function (obj) { // 参数obj是须要操做的对象 // 这里的this是指obj对象须要借用的方法,好比示例中的Array.prototype.push const fnObj = this return function (...args) { // 难点,如下代码至关于:fnObj.call(obj, ...args), 没理解请看下面的 “代码解析” 部分 return Function.prototype.call.apply(fnObj, [obj, ...args]) } } // 示例,导出Array.prototype.push方法给对象使用 const obj = { a: 'aa' } const push = Array.prototype.push.uncurrie(obj) push('b') push('c') console.log(obj) // {0: "b", 1: "c", a: "aa", length: 2}
这部份内容负责解析上面的
通用代码
ajax
- 首先声明,我的以为这个通用代码是不必的,由于这段通用代码的本质就是
call、apply
,经过call、apply改变方法的this上下文,使得对象可使用不属于它的方法,这也是反柯里化的本质,加强方法的使用范围- 这段通用代码的难点在于
Function.prototype.call.apply(fnObj, [obj, ...args])
这句,如下解析采用通用代码
中的示例代码
- 如下解释须要你熟悉
apply、call
方法的源码实现,若是不熟悉请参考 javascript源码解析,里面的call、apply两部分的源码解析会回答你的疑问- 正式开始解析
通用代码
,经过通用代码
中的示例
代码进行讲解通用代码
其实就是个闭包,执行Array.prototype.push.uncurrie(obj)
,传递一个须要操做的对象(const obj = {a: 'aa'}),其中fnObj = Array.prototype.push
,这时向外面return一个接收参数的函数- 返回的函数中就一句代码:
return Function.prototype.call.apply(fnObj, [obj, ...args])
,缓存
- 上面的代码能够翻译为:
return Function.prototype.call.apply(Array.prototype.push, [{a: 'aa'}, ...args])
闭包
- 再进一步翻译(须要了解call、apply的原理,不明白请参考javascript源码解析):
return Array.prototype.push.call({a: 'aa'}, ...args)
,这句就等同于:Arrray.prototype.push.call(obj, 'b')
,看到这里就会明白我开始说的 “声明” 部分的意思了app
柯里化其实就是特殊的偏函数,偏函数的本质就是经过调用函数,预置一部分参数,而后返回一个参数更少但针对性更强的函数;而反柯里化,不知道为啥叫反柯里化,感受应该叫反偏函数更好一点,反柯里化做用和偏函数相反,它的本质是加强一个函数的使用范围,让一个对象可使用不属于对象本身的方法,就像apply、call、bind(也有偏函数的做用)的做用,而事实上反柯里化就是经过apply、call方法实现的