[英]Functional Programming - .bind.apply for curry function
閱讀函數式編程- 開始柯里化,示例有一個簡單的柯里化功能。 我理解除了最后一個else
塊之外的所有else
。
var curry = function (fn, fnLength) {
fnLength = fnLength || fn.length;
return function () {
var suppliedArgs = Array.prototype.slice.call(arguments);
if (suppliedArgs.length >= fn.length) {
return fn.apply(this, suppliedArgs);
} else if (!suppliedArgs.length) {
return fn;
} else {
return curry(fn.bind.apply(fn, [this].concat(suppliedArgs)), fnLength - suppliedArgs.length);
}
};
};
如果提供的參數>=
,則使用提供的參數調用函數。
否則,如果suppliedArgs.length
是falsy,恢復原有的功能,而不做任何事情。
別的 ???
[this]
只是在一個數組中,因為 providedArgs.push 不會返回該數組?首先看看你如何調用Function#bind()
:
fun.bind(thisArg[, arg1[, arg2[, ...]]])
然后考慮如何使用Function#apply()
:
fun.apply(thisArg, [argsArray])
因此,對於給定的bind()
,我們需要調用它的函數,並給它多參數(不是數組),而我們擁有的是參數(數組suppliedArgs
代碼),那么,我們怎樣才能做到這一點? 好吧,您可以調用帶有多個參數而不是作為數組的單個參數的函數的主要方法是在函數上使用.apply()
。 那么我們有fn.bind.apply(...something...)
。
.apply()
的第一個參數是this
值 - 在.bind()
的情況下,它需要是要綁定的函數(有關原因的解釋,請參見下文)。 因此fn.bind.apply(fn, ...)
。
然后, .apply()
的第二個參數是您正在調用的函數的所有參數的數組,在.bind()
的情況下是thisArg[, arg1[, arg2[, ...]]]
。 因此,我們需要一個數組,第一個值是函數中this
的值,然后是其他參數。 這就是[this].concat(suppliedArgs)
產生的。
因此,整個fn.apply.bind(fn, [this].concat(suppliedArgs))
事物會生成一個正確綁定的函數,該函數將為當前函數“預fn.apply.bind(fn, [this].concat(suppliedArgs))
”提供參數,並具有正確的this
上下文。 生成的這個函數然后在遞歸調用中作為fn
參數傳遞給curry()
,這反過來會以與頂級調用相同的方式生成另一個函數。
總體效果是,無論何時調用由curry()
創建的函數,如果沒有傳遞預期數量的參數,您將獲得一個采用剩余參數數量的新函數,或者您將評估原始函數正確傳入的整個參數列表。
例如
function addAllNums(a, b, c, d, e) {
return a + b + c + d + e;
}
var curriedAddAll = curry(addAllNums, 5);
var f1 = curriedAddAll(1); // produces a function expecting 4 arguments
var f2 = f1(2, 3); // f2 is a function that expects 2 arguments
var f3 = f2(4); // f3 is a function that expects 1 argument
var f4 = f3(5); // f4 doesn't expect any arguments
var ans = f4(); // ans = 1 + 2 + 3 + 4 + 5 = 15.
// OR var ans = f3(5); => same result
thisArg
值不同? 這行代碼最令人困惑的地方可能是.bind()
和.apply()
thisArg
的兩個不同值。
對於.apply()
, thisArg
是您希望this
的值在您調用.apply()
的函數內的值。 例如myFunction.apply(myObj, ['param1', 'param2'])
相當於myObj.myFunction('param1', 'param2')
。
在這種特殊情況下, .bind()
在fn
函數上執行,所以我們希望fn
是.bind()
的this
值,因此它知道它正在創建哪個函數的綁定版本。
對於.bind()
, thisArg
是this
的值在返回的綁定函數中的值。
在我們的例子中,我們想要返回一個綁定函數,它與我們當前擁有的this
值相同。 換句話說,我們希望在新函數中正確維護this
值,因此它不會在您創建新函數時丟失,當您調用帶有少於預期的參數的柯里化函數時會發生這種情況。
如果我們沒有正確維護this
值,以下示例將不會記錄this
的正確值。 但是通過維護它,將輸出正確的值。
var myObj = {
a: 1,
b: curry(function (a, b, c, d) {
console.log('this = ', this);
return a + b + c + d;
})
};
var c = myObj.b(1,1,1); // c is a function expecting 1 argument
c(1); // returns 4, and correctly logs "this = Object {a: 1, b: function}"
// if "this" wasn't maintained, it would log the value of "this" as
// the global window object.
最后一個 else 塊是curry
函數的主要和最重要的部分,因為它是承載 currying 邏輯的實際行。
return curry(fn.bind.apply(fn, [this].concat(suppliedArgs)), fnLength - suppliedArgs.length);
這就是返回需要從前一個函數中獲取 n-1 個參數的新函數的原因。 為什么? 它是多種事物的組合:
fn.bind.apply
只是在函數本身的上下文中調用函數,同時提供所需的參數 (suppliedArgs)。 注意 curry 的下一個參數是 fnLength - providedArgs.length,它減少了傳遞的參數所需的參數。
讓我們在 ES6 的幫助下解釋它。 事情將變得更加明顯。
// Imagine we have the following code written in ES5
function fn(a, b, c) {
console.log(a, b, c);
}
var arr = [1, 2, 3];
var funcWithBoundArguments = fn.bind.apply(fn, [null].concat(arr));
讓我們將 ES5 轉換為 ES6 代碼
// ES6
function fn(a, b, c) { console.log(a, b, c) }
let arr = [1,2,3];
let funcWithBoundArguments = fn.bind(null, ...arr)
你看? 當您綁定一個函數時,我們必須顯式枚舉所有參數,例如:
fn.bind(null, 1, 2, 3)
但是如果我們事先不知道它的內容,我們怎么能綁定一個數組的內容呢?
是的,我們必須使用.bind.apply()
其中:
apply
的第一個參數是我們綁定的函數( fn
)fn
)的參數(該數字是可變的) .
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.