這一次來學習一下Partial Application。我們先看一下函數的介紹,在維基上有簡單的介紹:
在數學中,一個函數是描述每個輸入值對應唯一輸出值的這種對應關係,符號為 f(x)。例如,運算式 f(x)=x2表示了一個函數 f,其中每個輸入值x都與唯一輸出值x2相聯絡。
因此,如果一個輸入值為3,那麼它所對應的輸出值為9。而g(x,y) = xy有兩個參量x和y,以乘積xy為值。上面描述了函數(為方便假設x,y都是int),並且給出了函數的兩個例子,先換一種方式來看,f(x)可以表示為:x -> y(x2),即經經過f到x2的映射,寫成 int -> int。
接受一個int 返回一個int。再看g(x,y)可以表示為:x -> y -> z(xy)。即x,y經過g的映射到z,寫成 int -> int -> int。我們看g(x,y)函數,用javascript來實現一下: 複製代碼 代碼如下:function g(x,y){
return x*y;
}
很完美啊,很接近數學定義。它依次接受兩個參數,x與y。並且返回它們兩個的乘積。但是當x是個常數,比如x=n(n是一個自然數)。那麼g(n,y)=ny。這就變成一個常數與一個變數的乘積,它接受一個參數y返回ny,即y -> z(ny) 的映射,寫成 int -> int。因此,我們可以這樣來理解上面的工作,g(x,y)是接受一個參數int,並且返回一個函數 int ->int 。這個返回的函數只接受一個int 並且返回一個int。來用javascript表示一下: 複製代碼 代碼如下:var h = g(2);
這裡的h表示函數h(y)=2y。這樣就有h(5)=10,h(13)=26等。 複製代碼 代碼如下:h(5);
h(13);
這個技術是把需要多個參數的函數形式轉變為接受單個參數的函數鏈,它通常叫做Curring,這是為了紀念Haskell Curry而起的名字,但他並不是第一個提出的1。但是很遺憾的是javascript並不支援這樣的特性。所以要實現這樣的特性需要做一些工作,這些工作並不複雜。主要是把參數儲存起來,等待調用函數鏈上的下一個函數時拿出前邊參數繼續傳遞給鏈上的下一個函數,直到最後得到傳回值。先看一下下面的代碼: 複製代碼 代碼如下:function atarr(a,index){
var index=index||0,args = new Array(a.length - index);
for(var i in a){
if(i>=index) args[i-index]=a[i];
}
return args;
}
function m(scope,fn){
if(arguments.length<3) return fn.call(scope);
var p = atarr(arguments,2);
return function(){
var args = atarr(arguments);
return fn.apply(scope,p.concat(args));
}
}
測試代碼: 複製代碼 代碼如下:var plus = function(a,b){
return a+b;
};
var plus2 = m(null,plus,2);
console.log(plus2(10));
console.log(plus2(0));
//結果
12
2
這樣我們的目標已經實現啦。在上面的atarr函數是將arguments對象中指定位置開始的參數取出並且儲存到一個數組中。m函數就是主角,它完成了前面定義的任務,實現了儲存函數鏈上的參數並且返接受餘下參數的函數。測試代碼中的plus函數原先接受a,b兩個參數並返回a與b之和,即 int -> int -> int,而plus2則變成了接受一個參數b與2相加,並返回2與b之和,即 int -> int。
通過上面的一些工作,我們實現了javascript中的Partial Application,在dojo架構中hitch2實現了域綁定和partial。有興趣可以讀一下它的源碼,也是非常簡單明了的。