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);
// ...
難點:
- 預存儲第一次調用的參數
- 第二次調用時,兩次傳入的參數合併
因爲需要調用兩次,因此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實現
- bind特點
- 返回一個函數
- 可以傳入參數
bind() 方法會創建一個新函數。當這個新函數被調用時,bind() 的第一個參數將作爲它運行時的 this,之後的一序列參數將會在傳遞的實參前傳入作爲它的參數。(來自於MDN)
- 實例
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深入瞭解》