js高級進階之函數柯里化

函數柯里化是所有編程語言推崇的函數優化方式,js的函數柯里化是你寫出優雅函數的基礎。

概念

在計算機科學中,柯里化(Currying)是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,並且返回接受餘下的參數且返回結果的新函數的技術。
例如:我們求(a+b)*c 如果普通方法實現 f(a,b,c),用柯里化方法:f(a)(b)©這種調用格式獲取。

實現原理

  • 閉包來保存參數;
  • 高階函數實現運算;

例如最簡單的例子:求 (15+3)*4的值

let calcu = (a, b, c) => (a+b)*c;
        function curry(fn, args){
            let len = fn.length;
            let _this = this;
            let _args = args || [];
            return function(){
                let args = Array.prototype.slice.apply(arguments);
                args = Array.prototype.concat.call(_args, args);
                // 當接收到的參數小於fn所需參數個數時,繼續接收參數
                if(args.length < len){
                    return curry.call(_this, fn, args);
                }
                return fn.apply(this, args);
            }
        }
        let add = curry(calcu);
        console.log(add(15)(3)(5)); //72

在上面的代碼中,我們使用curry函數它可以不用一次性接收三個參數,而是慢慢接收,當發現接收到的參數達到3個之後再返回結果。這就是參數複用(通過例子我們可以看到,實現這個特點主要原因是利用了閉包,將接收到的參數存於_args中,由於閉包的原因這些參數在函數執行完之後並不會被釋放掉。

上面的curry方法,將每次調用fn時讀入的參數用args來保存,並將本次讀入的參數args與當前一共擁有的所有參數_args用concat方法連接起來,當參數個數符合fn的參數個數要求時,則調用fn。

特點

  • 參數複用: 就是利用閉包的原理,讓我們前面傳輸過來的參數不要被釋放掉。
  • 提前確認: 利用柯里化特性對某些全局方法進行改寫,這樣其實就是提前確定了會走哪一個方法,避免每次都進行判斷。
  • 延遲運行:利用閉包將函數的定義和執行環境分開。

參數複用案例:

// 正常正則驗證字符串 reg.test(txt)

// 函數封裝後
function check(reg, txt) {
    return reg.test(txt)
}

check(/\d+/g, 'test')       //false
check(/[a-z]+/g, 'test')    //true

// Currying後
function curryingCheck(reg) {
    return function(txt) {
        return reg.test(txt)
    }
}

var hasNumber = curryingCheck(/\d+/g)
var hasLetter = curryingCheck(/[a-z]+/g)

hasNumber('test1')      // true
hasNumber('testtest')   // false
hasLetter('21212')      // false

提前確認:

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);
    }
}

延遲運行:

Function.prototype.bind = function (context) {
    var _this = this
    var args = Array.prototype.slice.call(arguments, 1)
 
    return function() {
        return _this.apply(context, args)
    }
}

柯里化函數的最佳實現

函數柯里化是一個比較難以應用的方法,最主要是能夠理解其原理使你閱讀源碼無障礙,下面給出一個柯里化的最佳實現方法,背過他可以救急

// 支持多參數傳遞
function progressCurrying(fn, args) {
    var _this = this
    var len = fn.length;
    var args = args || [];

    return function() {
        var _args = Array.prototype.slice.call(arguments);
        Array.prototype.push.apply(args, _args);

        // 如果參數個數小於最初的fn.length,則遞歸調用,繼續收集參數
        if (_args.length < len) {
            return progressCurrying.call(_this, fn, _args);
        }

        // 參數收集完畢,則執行fn
        return fn.apply(this, _args);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章