函数柯里化
把接收多个参数的函数变换成接收一个单一参数(最初函数的第一个参数)的函数,并返回接受剩余的参数而且返回结果的新函数的技术。
简单的说,柯里化函数持续地返回一个新函数直到所有的参数用尽为止。这些参数全部保持“活着”的状态(通过闭包),然后当柯里化链中的最后一个函数被返回和执行时会全部被用来执行。
提高函数的适用性,同时降低函数的通用性;其实现方式就是固定一些可以预期的参数,然后返回一个特定的函数
简单的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function add(x, y) { return x + y }
function curryingAdd(x) { return function (y) { return x + y } }
add(1, 2) curryingAdd(1)(2)
|
函数柯里化的好处
- 参数复用:提前绑定好函数里面的某些参数,达到参数复用的效果,提高了适用性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
function check(reg, txt) { return reg.test(txt) }
check(/\d+/g, 'test') check(/[a-z]+/g, 'test')
function curryingCheck(reg) { return function(txt) { return reg.test(txt) } }
var hasNumber = curryingCheck(/\d+/g) var hasLetter = curryingCheck(/[a-z]+/g)
hasNumber('test1') hasNumber('testtest') hasLetter('21212')
|
- 提前返回
1 2 3 4 5 6 7 8 9 10 11 12
| var addEvent = function(element, event, handler) { if (document.addEventListener) { if (element && event && handler) { element.addEventListener(event, handler, false); } } else { if (element && event && handler) { element.attachEvent('on' + event, handler); } } }
|
上面的方法有什么问题呢?很显然,我们每次使用addEvent为元素添加事件的时候,会走一遍if…else if …,其实只要一次判定就可以了,怎么做?–柯里化。它相对一第一种写法就是自执行然后返回一个新的函数,这样其实就是提前确定了浏览器支持的事件绑定,避免每次都进行判断。改为下面这样子的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| var addEvent = (function() { if (document.addEventListener) { return function(element, event, handler) { if (element && event && handler) { element.addEventListener(event, handler, false); } }; } else { return function(element, event, handler) { if (element && event && handler) { element.attachEvent('on' + event, handler); } }; } })();
|
与 call/apply 方法直接执行不同,bind 方法将第一个参数设置为函数执行的上下文,其他参数依次传递给调用方法(函数的主体本身不执行,可以看成是延迟执行),并动态创建返回一个新的函数, 这符合柯里化特点
1 2 3 4 5 6
| Function.prototype.bind = function (target) { const self = this; return function () { self.apply(target, arguments) } }
|
函数柯里化实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| function currying(fn, args = []) { return function (..._) { args.push(..._); if (args.length < fn.length) { return currying(fn, args) } else { return fn(...args) } } }
function add(a, b, c, d) { return a + b + c + d; }
const fn = currying(add)(1, 3, 4) console.log(fn(2))
const isType = (type, content)=>{ return Object.prototype.toString.call(content) === `[object ${type}]`; }
const isString = currying(isType)('String'); console.log(isString('111'));
Function.prototype.uncurrying = function(){ return (...args)=>{ return this.call(...args) } }
const checkType = Object.prototype.toString.uncurrying(); console.log(checkType('111'))
|
函数反柯里化实现
参考
https://segmentfault.com/a/1190000006096034
https://www.jianshu.com/p/2975c25e4d71