一、介紹
函數柯里化(currying)又稱部分求值,一個currying函數首先接受一部分參數,該函數不會立即求值,而是返回另外一個函數(高階函數),剛纔傳入的參數通過閉包存起來,當真正需要求值時,一次性返回。
二、案例
函數求值
普通函數
function add (x, y) {
return x + y
}
add(1,2)
我們用柯里化實現
function add(x) {
return function (x, y) {
return x + y
}
}
add(1)(2) // 3
可以看到當我們傳入第一個參數時,會返回一個新的函數去處理剩下的參數
利用箭頭函數會更清晰
let add x => y => x + y
add(1)(2) // 3
三、封裝
我們寫一個柯里化的通用版本
let currying = function (fun) {
// arguments是一個類數組,需要轉成真實數組
let args = Array.prototype.slice.call(arguments,1)
return function () {
let _args = Array.prototype.sliice.call(arguments)
fun.apply(this,args.concat(_args))
}
}
function add (...args) {
let result = args.reduce((total,item) => total + item , 0)
console.log(result)
}
let _add = currying(add,1,2)
_add(3,4) // 10
通過上面代碼我們發現,柯里化之後,_add函數只能執行一次,如果我們想多次執行_add(1)(2)(3),會報錯,因爲我們返回一個值,而不是一個函數。
類似情況,我們首先想到採用遞歸的方式,如果currying之後調用的函數有參數,我們就繼續遞歸執行,反之執行結束
let currying = function (fun, arr = []) {
// 取出執行時參數,首次執行截掉function
let args = Array.prototype.slice.call(arguments,1) || arr
// 閉包 保存結果
return function () {
// 再次調用時的參數
let _args = Array.prototype.slice.call(arguments)
// 如果參數不存在,執行函數,反之繼續遞歸執行
if (_args.length == 0){
return fun.apply(this,args.concat(_args))
}else {
return currying.call(this,fun,...args.concat(_args))
}
}
}
function add (...args) {
let result = args.reduce((total,item) => total + item , 0)
console.log(result)
}
let _add = currying(add)
_add(2)(5)(8)(1)() // 16
_add(2,5,8)(1)() // 16
以上就是函數柯里化的內容了,理解起來比較費勁,編程思想很贊!
注意:
1、arguments是一個類數組(非數組對象),需要用Array.prototype.slice.call(arr)轉成真數組,也可以使用ES6提供的Array.from()處理: Array.prototype.slice.call(arr) === Array.form()
2、ES6規定function可以有length,代表當前函數的參數數量,如果參數設置默認值或者使用擴展運算符,length爲0 (…args / args = [])