函式柯里化
阿新 • • 發佈:2020-09-23
函式柯里化(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 13alert(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函式柯里化