javascript柯里化

1.什麼是柯里化?

柯里化概念其實很簡單,只傳遞給函數一部分參數來調用它,讓它返回一個函數去處理剩下的參數。

實例:

function add(a, b, c, d) {
    return a + b + c + d;
}
//通常調用方式
var sum = add(1, 2, 3, 4);
//柯里化的調用方式
var curryAdd = Curry(add);
var sum = curryAdd(1)(2)(3)(4);
//或者很多奇怪的方式調用
var sum = curryAdd(1, 2)(3, 4);
var sum = curryAdd(1, 2, 3)(4);
var sum = curryAdd(1)(2, 3, 4);
//...諸如此類

2.分析

2.1 柯里化簡化版本分析

首先我們考慮一種比較簡單的情況:兩次調用以內必須傳遞函數所需的所有參數。

//諸如此類調用方式
var curryAdd = Curry(add);
var sum = curryAdd(1,2)(3,4);
var sum = curryAdd(1,2,3)(4); 
//  ...

難點:

  1. 預存儲第一次調用的參數
  2. 第二次調用時,兩次傳入的參數合併

因爲需要調用兩次,因此subCurry需要返回一個函數(思想類似與jq的鏈式調用)

function subCurry (fn) {
    //取出第一次調用時傳入的參數
    //取出除了fn之外的參數
    var args = [].slice.call(arguments, 1);
    
    //第二次調用
    return function () {
        //參數拼接
        //args.concat([].slice.call(arguments,0))
        //由於事先規定兩次調用參數必須傳遞達到fn調用需求
        //因此直接調用函數fn
        return fn.apply(this, args.concat([].slice.call(arguments,0)));
    }
}

function add (a, b, c, d) {
    return a + b + c + d;
}
var myAdd = subCarry(add, 1 , 2)
console.log(myAdd(3, 4)) //10

2.2 柯里化全面版本

//獲取形參個數的方式: 函數名.length
//獲取實參個數的方式:arguments.length
function curry (fn, length) {
    length = length || fn.length;
    //如果實參個數等於滿足函數調用的形參個數
    //執行函數
    return function () {
    	if (arguments.length >= length) {
	        return fn.apply(this, arguments);
	    }else { 
	        //如果實參個數不等於滿足函數調用的形參個數
	        //因此需要再次傳遞參數,直至達到要求
	        
	        //那麼再一次傳遞參數,要執行什麼樣的流程那?
	        //1. 新傳入的參數和已經傳入參數進行拼接
	        //   subCurry
	        //2. 拼接之後再次檢查是否達到要求
	        //   curry
	        //3. 如果達到要求,執行函數
	        //4. 如果沒有達到要求,再次執行1.2.3步驟(遞歸)
	        var combined = [fn].concat([].slice.call(arguments, 0));
	        return curry(subCurry.apply(this, combined), length - arguments.length);
	    }
    }
}

3.原生JS模擬bind實現

  1. bind特點
    • 返回一個函數
    • 可以傳入參數

bind() 方法會創建一個新函數。當這個新函數被調用時,bind() 的第一個參數將作爲它運行時的 this,之後的一序列參數將會在傳遞的實參前傳入作爲它的參數。(來自於MDN)

  1. 實例
var a = 1;
var obj = {
    a: 2,
    add: function (x, y) {
        console.log(this.a + x + y);
    }
}
var add = obj.add;
add(2, 3)//6
var newAdd = add.bind(obj, 2);
newAdd(3)//7 

var newAdd2 = add.bind(obj)
newAdd2(2, 3)//7 

是不是感覺上述代碼的調用方式有點眼熟?

很明顯,就是subCurry的思想。

直接上代碼

Function.prototype.myBind = function (context) {
    //預存儲第一次傳入的參數
    var self = this;
    var args = [].slice.call(arguments, 1);
    return function () {
        var combined = args.concat([].slice.call(arguments,0));
        self.apply(context, combined);
    }
}

除上述特點之外,bind還有一個比較麻煩的特點,更完善的bind原生模擬實現見《call,apply,bind深入瞭解》

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