1. 程式人生 > 實用技巧 >函式柯里化

函式柯里化

函式柯里化(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

函式柯里化的效能問題:

  1. 存取arguments物件通常要比存取命名引數要慢一點
  2. 一些老版本的瀏覽器在arguments.length的實現上是相當慢的
  3. 使用fn.apply()和fn.call()通常比直接呼叫fn()稍微慢點
  4. 建立大量巢狀作用域和閉包函式會帶來開銷,無論是在記憶體上還是速度上。

一道經典面試題

 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函式柯里化