1、一些術語
1.1 一元函式
只接受一個引數的函式稱為一元函式
const identity = (x) => x
複製程式碼
1.2 二元函式
接受兩個引數的函式
const add = (x,y) => x + y
複製程式碼
1.3 變參函式
接受可變數量引數的函式
// es5
function variadic(a){
console.log(a)
console.log(arguments)
}
// es6
const variadic = (a, ...variadic) => {
console.log(a)
console.log(variadic)
}
複製程式碼
2、什麼是柯里化?
柯里化就是把一個多引數函式轉換為一個巢狀的一元函式的過程。
呵呵,定義看似簡單易懂。但還是讓人感覺鏡中花,水中月。摸不透柯里化的本質。不要急,看官們。讓我們先從簡單的二元函式add
柯里化開始。
2.1 二元函式柯里化
const addCurried = x => y => x + y
// 其實就是閉包
function addCurried (x){
return function (y){
return x + y
}
}
addCurried(4)(3) // 7
複製程式碼
上面是手動地把接受二元函式add
轉換為含有巢狀的一元的函式。下面展示如何把該處理過程轉換為一個名為curry
的方法
// es5
const curry = (fn){
return function (firstParam){
return function (secondParam){
return fn(firstParam, secondParam)
}
}
}
// es6
let curry = fn => firstParam => secondParam => fn(firstParam, secondParam)
複製程式碼
至此,對於函式柯里化,我們應該揭開了TA神祕的面紗。 但是大多數我們寫的函式都是變參的。直接看下面變參函式柯里化
2.2 變參函式柯里化
2.2.1 簡單變參函式柯里化
let curry = (fn) => {
if(typeof fn !== 'function'){
throw Error ('引數必須是函式')
}
return function curriedFn(...args){
return fn.apply(null,args)
}
}
複製程式碼
如果我們有個multiply
函式
// es 6
const multiply = (x,y,z)=> x*y*z
複製程式碼
我們可以通過以下方式使用2.2中的curry
函式
curry(multiply)(1,2,3) // 6
複製程式碼
下面我們看看,curry
是如何執行的。
我們在curry
函式裡新增了如下程式碼:
return function curriedFn(...args){
return fn.apply(null,args)
}
複製程式碼
返回函式是一個變參函式,它返回了傳入args
並通過apply
呼叫函式的結果
fn.apply(null, args)
複製程式碼
通過curry(multiply)(1,2,3)
,args
將會指向[1,2,3]
,由於我們呼叫了fn
的apply
,等價於:
multiply(1,2,3)
複製程式碼
通過2.2 部分我們實現了變參函式的柯里化,接下來我們實現把多參函式轉換為一元函式的curry
函式,就如函式柯里化的定義一樣。
2.2.2 轉換為巢狀的一元函式的curry
函式
let curry = (fn)=>{
if(typeof fn !== 'function'){
throw Error('引數必須是函式')
}
return function curriedFn(...args){
if(args.length < fn.length){
return function(){
return curriedFn.apply(null,args.concat([].slice.call(arguments)))
}
}
return fn.apply(null args)
}
}
複製程式碼
和之前的curry
函式相比我們新增了
if(args.length < fn.length){
return function(){
return curriedFn.apply(null,args.concat([].slice.call(arguments)))
}
}
複製程式碼
讓我們逐句理解在這段程式碼中發生了什麼
args.length < fn.length
複製程式碼
這行程式碼是檢查通過...args
傳入的引數長度是否小於函式fn
引數列表的長度。如果是,就進入if
程式碼塊,如果不是,就如之前一樣呼叫整個函式fn
。
一旦進入if
程式碼塊,就使用apply
函式遞迴地呼叫curriedFn
函式:
curriedFn.apply(null,args.concat([].slice.call(arguments)))
複製程式碼
此程式碼片段中:
args.concat([].slice.call(arguments))
複製程式碼
非常重要,我們使用concat
函式連線每次傳入的引數,並遞迴地呼叫curriedFn
。由於我們將所有傳入的引數組合並遞迴呼叫,終將
if(args.length < fn.length)
複製程式碼
條件失敗,即引數列表長度args
和函式引數的長度fn.length
相等。程式將呼叫整個函式fn
return fn.apply(null args)
複製程式碼
這將產生函式的完整的結果。
好了,函式柯里化就說到這了。 噗。。。感覺寫的不好,大佬們請多多關照