函数柯里化:能够将接收多个参数的函数转化为接收单一参数的函数,并且能返回接收余下参数且返回结果的新函数(只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数)
1 // 普通的add函数 2 function add(x, y) { 3 return x + y 4 } 5 6 // Currying后 7 function curryingAdd(x) { 8 return function (y) { 9 return x + y 10 } 11 } 12 13 add(1, 2) // 3 14 curryingAdd(1)(2) // 3
实际上是将add函数的两个参数x、y转换成先用一个函数接收参数x,并且返回一个函数去处理余下的参数y。
进一步
// 实现一个求和函数构造器,使得当该函数被链式调用n次后,返回求和的值 function curryingSum (n) { var result = 0; function callback (num) { n--; result += num; if(n > 0) { return callback; } else { return result; } } return callback; } // useage var add = curryingSum(4) var result = add(1)(2)(3)(4); console.log(result); // 10 // or var add = curryingSum(6) add = add(1) add = add(2)(3) add = add(4)(5)(6) console.log(result); // 21
思考:如果不提供终止条件n,函数应该怎么实现?????
通用版
function curry(fn) { var args = Array.prototype.slice.call(arguments, 1); return function() { var newArgs = args.concat(Array.prototype.slice.call(arguments)); return fn.apply(this, newArgs) } }
curry函数的第一个参数是要动态创建柯里化的函数,余下的参数存储在args变量中。
执行 curry 函数返回的函数接收新的参数与 args 变量存储的参数合并,并把合并的参数传入给柯里化了的函数。
function add(a, b, c) { return a + b + c; } var multi = curry(add); multi(2,3,4); //9
但是以上代码无法实现add(2)(3)(4)
优化版
function curry(fn, args) { var _args = args || [], len = fn.length; return function() { // 以下语句不可取 // _args = _args.concat(Array.prototype.slice.call(arguments)) var newArgs = _args.concat(Array.prototype.slice.call(arguments)); if(newArgs.length < len) { return curry.call(this, fn, newArgs); } else { return fn.apply(this, newArgs); } } } function add(a, b, c) { return a + b + c; } var curryAdd = curry(add); curryAdd(1,2,3) //6 curryAdd(1,2)(3) //6 curryAdd(1)(2,3) //6 curryAdd(1)(2)(3) //6
以上实现必须事先知道参数的个数,为了让代码更灵活,达到随意传参的效果又该怎么做呢???对应了以上的思考
扩展版
function add() { // 第一次执行时,定义一个数组专门用来存储所有的参数 var _args = Array.prototype.slice.call(arguments); // 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值 var _adder = function() { _args.push(...arguments); return _adder; }; // 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回 _adder.toString = function () { return _args.reduce(function (a, b) { return a + b; }); } return _adder; } add(1)(2)(3) // 6 add(1, 2, 3)(4) // 10 add(1)(2)(3)(4)(5) // 15 add(2, 6)(1) // 9
1)参数复用
// 正常正则验证字符串 reg.test(txt) // 函数封装后 function match(reg, txt) { return reg.test(txt) } //curry之后 function curryMatch(reg) { return function(txt) { return reg.test(txt) } } var hasNumber = curryMatch(/\d+/g) var hasLetter = curryMatch(/[a-z]+/g) hasNumber(‘test1‘) // true hasNumber(‘testtest‘) // false hasLetter(‘21212‘) // false
一般可以直接调用match方法进行正则校验;但是可能很多地方用到校验数字的地方,此时可以复用reg参数生成hasNumber函数,直接调用hasNumber便可以
2)提前确认
var on = 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); } } } var on = (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); } }; } })(); //换一种写法可能比较好理解一点,上面就是把isSupport这个参数给先确定下来了 var on = function(isSupport, element, event, handler) { isSupport = isSupport || document.addEventListener; if (isSupport) { return element.addEventListener(event, handler, false); } else { return element.attachEvent(‘on‘ + event, handler); } }
第一种写法也是比较常见,但是第二种写法相对一第一种写法就是自执行然后返回一个新的函数,这样其实就是提前确定了会走哪一个方法,避免每次都进行判断
3)延迟执行
Function.prototype.bind = function (context) { var _this = this var args = Array.prototype.slice.call(arguments, 1) return function() { return _this.apply(context, args) } }
像我们js中经常使用的bind,实现的机制就是Currying.
原文:https://www.cnblogs.com/sinlyfly/p/11655816.html