函數柯里化

含義:柯里化(Currying)是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,並且返回接受餘下的參數且返回結果的新函數的技術。
因此柯里化的過程是逐步傳參,逐步縮小函數的適用範圍,逐步求解的過程。

柯里化一個求和函數

var add = function (a, b, c) {
    return a+b+c;
};

var addCurrying= function(a) {
    return function (b) {
        return function (c) {
            return a+b+c;
        };
    };
};
console.log(add(1,2,3));  //6
console.log(addCurrying(1));//function
console.log(addCurrying(1)(2));//function
console.log(addCurrying(1)(2)(3)); //6

這裏寫圖片描述

可以看到, addCurrying(1) 是一個 Function,每次調用都返回一個新的函數,該函數接受另一個調用,然後又返回一個新的函數,直至最後返回結果,分佈求解,層層遞進。(PS:這裏利用了閉包的特點)

那麼現在我們更進一步,如果要求可傳遞的參數不止3個,可以傳任意多個參數,當不傳參數時輸出結果?

首先來個普通的實現:

//歸併
var add = function(items){
    return items.reduce(function(a,b){
        return a+b
    });
};
console.log(add([1,2,3,4]));   //10

但如果要求把每個數乘以10之後再相加,那麼:

var add = function (items,multi) {
    return items.map(function (item) {
        return item*multi;
    }).reduce(function (a, b) {
        return a + b
    });
};
console.log(add([1, 2, 3, 4],10));  //100

好在有 map 和 reduce 函數,假如按照這個模式,現在要把每項加1,再彙總,那麼我們需要更換map中的函數。

下面看一下柯里化實現:

var add = function () {
    var _args = [];
    return function () {
        if (arguments.length === 0) {
            return _args.reduce(function (a, b) {
                return a + b;
            });
        }
        [].push.apply(_args, [].slice.call(arguments));
        return arguments.callee;
    }
};    
var sum = add();

console.log(sum);     // Function

sum(100,200)(300);    // 調用形式靈活,一次調用可輸入一個或者多個參數,並且支持鏈式調用
sum(400);
console.log(sum());   // 1000 (加總計算) 

這裏寫圖片描述

上面 add是柯里化了的函數,它返回一個新的函數,新的函數接收可分批次接受新的參數,延遲到最後一次計算。

通用的柯里化函數

var curry = function(fn) {
    var _args = []
    return function cb() {
        if (arguments.length == 0) {
            return fn.apply(this, _args)
        }
        Array.prototype.push.apply(_args, arguments);
        return cb;
    }
}

柯里化的基礎

上面的代碼其實是一個高階函數(high-order function), 高階函數是指操作函數的函數,它接收一個或者多個函數作爲參數,並返回一個新函數。此外,還依賴與閉包的特性,來保存中間過程中輸入的參數。即:

1.函數可以作爲參數傳遞
2.函數能夠作爲函數的返回值
3.閉包

柯里化的作用

1.延遲計算。上面的例子已經比較好低說明了。

2.參數複用。當在多次調用同一個函數,並且傳遞的參數絕大多數是相同的,那麼該函數可能是一個很好的柯里化候選。

3.動態創建函數。這可以是在部分計算出結果後,在此基礎上動態生成新的函數處理後面的業務,這樣省略了重複計算。或者可以通過將要傳入調用函數的參數子集,部分應用到函數中,從而動態創造出一個新函數,這個新函數保存了重複傳入的參數(以後不必每次都傳)。例如,事件瀏覽器添加事件的輔助方法:

var addEvent = function(el, type, fn, capture) {
     if (window.addEventListener) {
         el.addEventListener(type, function(e) {
             fn.call(el, e);
         }, capture);
     } else if (window.attachEvent) {
         el.attachEvent("on" + type, function(e) {
             fn.call(el, e);
         });
     } 
 };

每次添加事件處理都要執行一遍 if…else…,其實在一個瀏覽器中只要一次判定就可以了,把根據一次判定之後的結果動態生成新的函數,以後就不必重新計算。

var addEvent = (function(){
    if (window.addEventListener) {
        return function(el, sType, fn, capture) {
            el.addEventListener(sType, function(e) {
                fn.call(el, e);
            }, (capture));
        };
    } else if (window.attachEvent) {
        return function(el, sType, fn, capture) {
            el.attachEvent("on" + sType, function(e) {
                fn.call(el, e);
            });
        };
    }
})();

這個例子,第一次 if…else… 判斷之後,完成了部分計算,動態創建新的函數來處理後面傳入的參數,這是一個典型的柯里化。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章