函数柯里化(function currying):是把接收多个参数的函数变换成接收一个单一参数(最初函数的第一个参数)的函数,并且返回接收余下的参数而且返回结果的新函数的技术。
解释有些抽象,先来看下面的例子:
1 //普通函数 2 function add(num1, num2) { 3 return num1 + num2; 4 } 5 6 //柯里化后 7 function curriedAdd(num1) { 8 return function(num2) { 9 return num1 + num2; 10 } 11 } 12 13 alert(add(2, 3); //5 14 alert(curriedAdd(3)(5)); //8
实际上,就是把add函数的num1,num2两个参数变成了先用一个函数接收num1参数然后返回一个函数去处理num2参数。
函数柯里化的用处:
1.参数复用
1 function check(reg, str) { 2 return reg.test(str); 3 } 4 5 check(/d+/g, 'test'); //false 6 check(/[a-z]+/g, 'test'); //true 7 8 function curryingCheck(reg) { 9 return function(str) { 10 return reg.test(str); 11 } 12 } 13 14 var hasNumber = curryingCheck(/d+/g); 15 16 hasNumber('test1'); //true 17 hasNumber('testtest'); // false 18 hasNumber('abc'); //false
上面的示例,就是对check()函数第一个参数reg进行了复用。
2.提前确认
1 var addEvent = function(element, event, handler) { 2 if(document.addEventListener) { 3 if(element && event && handler) { 4 element.addEventListener(event, handler, false); 5 } 6 } else { 7 if(element && event && handler) { 8 element.attachEvent('on' + event, handler); 9 } 10 } 11 } 12 13 14 var addEvent = (function() { 15 if(document.addEventListener) { 16 return function(element, event, handler) { 17 if(element && event && handler) { 18 element.addEventListener(event, handler, false); 19 } 20 }; 21 } else { 22 return function(element, event, handler) { 23 if(element && event && handler) { 24 element.attachEvent('on' + event, handler); 25 } 26 }; 27 } 28 })();
调用函数执行一次判断后返回一个函数,之后同一个用户就不用再进行判断了。
3.延迟运行
1 Function.prototype.bind = function(context) { 2 var _this = this; 3 var args = Array.prototype.slice.call(arguments, 1); 4 5 return function() { 6 return _this.apply(context, args); 7 } 8 }
我们在JavaScript中经常使用的bind()方法,实现的机制就是currying。
柯里化函数通常由以下步骤动态创建:调用另一个函数并为它传入要柯里化的函数和必要参数。下面是创建柯里化函数的通用方式:
1 function curry(fn) { 2 var args = Array.prototype.slice.call(arguments, 1); 3 4 return function() { 5 var innerArgs = Array.prototype.slice.call(arguments); 6 var finalArgs = args.concat(innerArgs); 7 return fn.apply(null, finalArgs); 8 } 9 }
实现一个curry()函数, 将普通函数柯里化
1 function curry(fn, args=[]) { 2 return function() { 3 let rest = [...args, ...arguments]; 4 if(rest.length < fn.length) { 5 return curry.call(this, fn, rest); 6 } else { 7 return fn.apply(this, rest); 8 } 9 } 10 } 11 12 function add(x, y, z) { 13 return a + b + c; 14 } 15 16 let sum = curry(add); 17 console.log(sum(1)(2)(3)); //6 18 console.log(sum(1)(2, 3)); //6
函数柯里化的性能问题:
- 存取arguments对象通常要比存取命名参数要慢一点
- 一些老版本的浏览器在arguments.length的实现上是相当慢的
- 使用fn.apply()和fn.call()通常比直接调用fn()稍微慢点
- 创建大量嵌套作用域和闭包函数会带来开销,无论是在内存上还是速度上。
一道经典面试题
1 //实现一个add方法,使计算结果能够满足如下预期: 2 3 add(1)(2)(3) = 6; 4 add(1, 2, 3)(4) = 10; 5 add(1)(2)(3)(4)(5) = 15; 6 7 function add() { 8 var _args = Array.prototype.slice.call(arguments); 9 10 var _adder = function() { 11 _args.push(...arguments); 12 return _adder; 13 }; 14 15 _adder.toString = function() { 16 return _args.reduce(function(a, b) { 17 return a + b; 18 }); 19 } 20 return _adder; 21 } 22 23 add(1)(2)(3) //6 24 add(1, 2, 3)(4) //10 25 add(1)(2)(3)(4)(5) //15 26 add(2, 6)(1) //9
参考链接:详解JS函数柯里化