javascript currying返回函數的函數_javascript技巧

來源:互聯網
上載者:User
最早期的curry函數有點多態的意味,就是根據函數參數在內部選用分支:
複製代碼 代碼如下:

//http://www.openlaszlo.org/pipermail/laszlo-user/2005-March/000350.html
// ★★On 8 Mar 2005, at 00:06, Steve Albin wrote:

function add(a, b) {

if (arguments.length < 1) {

return add;

} else if (arguments.length < 2) {

return function(c) { return a + c }

} else {

return a + b;

}

}



var myadd = add( 2 );

var total = myadd(3);


日本的一個先行者可能在未搞清arguments也能用Array的原生方法轉換為數組的時候,用非常複雜的正則與eval搞出一個更接近現代currying意味的函數。

複製代碼 代碼如下:

function curry(fun) {

if (typeof fun != 'function') {

throw new Error("The argument must be a function.");

}

if (fun.arity == 0) {

throw new Error("The function must have more than one argument.");

}

var funText = fun.toString();

var args = /function .*\((.*)\)(.*)/.exec(funText)[1].split(', ');

var firstArg = args.shift();

var restArgs = args.join(', ');

var body = funText.replace(/function .*\(.*\) /, "");

var curriedText =

"function (" + firstArg + ") {" +

"return function (" + restArgs + ")" + body +

"}";

eval("var curried =" + curriedText);

return curried;
}

function curry(fun) { if (typeof fun != 'function') { throw new Error("The argument must be a function."); } if (fun.arity == 0) { throw new Error("The function must have more than one argument."); } var funText = fun.toString(); var args = /function .*\((.*)\)(.*)/.exec(funText)[1].split(', '); var firstArg = args.shift(); var restArgs = args.join(', '); var body = funText.replace(/function .*\(.*\) /, ""); var curriedText = "function (" + firstArg + ") {" + "return function (" + restArgs + ")" + body + "}"; eval("var curried =" + curriedText); return curried; } function sum(x, y) { return x + y; } function mean3(a, b, c) { return (a + b + c)/3; } var a = curry(sum)(10)(15) alert(a)//25 var b = curry(mean3)(10)(20, 30); alert(b)//20 var c = curry(curry(sum))(10)()(20); alert(c); var d = curry(curry(mean3)(10))(20)(30); alert(d);
[Ctrl+A 全選 注:如需引入外部Js需重新整理才能執行]

接著是閉包的流行,與數群組轉換arguments的技術的發現,現代currying函數終於粉墨登場,就好像15~17世紀大航海時代的地理大發現,javascript的世界突然間開闊了許多。
複製代碼 代碼如下:

//一個簡單的現代currying函數
function curry (fn, scope) {
var scope = scope || window;
var args = [];
for (var i=2, len = arguments.length; i < len; ++i) {
args.push(arguments[i]);
};
return function() {
fn.apply(scope, args);
};
}

一般的currying函數只有兩重,執行情況如下,第一次執行參數不足返回內建函式,第二次執行才最終完成。不過針對這參數,我們還是可以做一些文章。看如下函數:
複製代碼 代碼如下:

function sum(){
var result=0;
for(var i=0, n=arguments.length; i<n; i++){
result += arguments[i];
}
return result;
}
alert(sum(1,2,3,4,5)); // 15

這就沒有所謂的參數不足問題,傳入一個參數,它也計算。但不傳入參數呢?無錯,區別在於有沒有參數。我們可以讓它不斷執行自身,如果參數存在的情況下。最後在沒有參數的情況下,一次過執行。換言之,前面的步驟是用於儲存參數。
var sum2= curry(sum);
sum2= sum2(1)(2)(3)(4)(5);
sum2(); // 15
比起一般的currying函數,這有點難度。具體看註解:
複製代碼 代碼如下:

var curry= function(fn){//原函數的參數為函數
return function(args){//內建函式的參數為數組,由於立即執行,因此直接到第三重去
//args是相對於第三重內建函式可是全域變數
var self= arguments.callee;//把自身儲存起來(就是那個數組為參數的第二重函數)
return function(){ //這才是第二次調用的函數
if(arguments.length){//如果還有要添加的參數
[].push.apply(args,arguments);//apply把當前傳入的所有參數放進args中
return self(args);
}else{
return fn.apply(this,args);//apply的第二參數為數組
}
}
}([]);
};

function sum(){ var result=0; for(var i=0, n=arguments.length; i<n; i++){ result += arguments[i]; } return result; }; var curry = function(fn){//原函數的參數為函數 return function(args){//內建函式的參數為數組,由於立即執行,因此直接到第三重去 var self= arguments.callee;//把自身儲存起來 return function(){ //這才是第二次調用的函數 if(arguments.length){//如果還有要添加的參數 [].push.apply(args,arguments); return self(args); } else return fn.apply(this,args);//執行 } }([]); }; var sum2= curry(sum); sum2= sum2(1)(2)(3)(4)(5); alert(sum2());
[Ctrl+A 全選 注:如需引入外部Js需重新整理才能執行]

或者每次傳入多個參數:
function sum(){ var result=0; for(var i=0, n=arguments.length; i<n; i++){ result += arguments[i]; } return result; }; var curry = function(fn){//原函數的參數為函數 return function(args){//內建函式的參數為數組,由於立即執行,因此直接到第三重去 var self= arguments.callee;//把自身儲存起來 return function(){ //這才是第二次調用的函數 if(arguments.length){//如果還有要添加的參數 [].push.apply(args,arguments); return self(args); } else return fn.apply(this,args);//執行 } }([]); }; var sum2= curry(sum); sum2= sum2(1,2,3); sum2= sum2(4,5,6); sum2= sum2(7,8,9); alert(sum2());
[Ctrl+A 全選 注:如需引入外部Js需重新整理才能執行]

但上面的函數有不足之處,最後怎麼也要放個括弧,我們想只要參數足夠就返回結果,多出的參數忽略。改進如下:
複製代碼 代碼如下:

function curry(f) {
if (f.length == 0) return f;
function iterate(args) {
if (args.length <= f.length)
return f.apply(null, args);
return function () {
return iterate(args.concat(Array.prototype.slice.call(arguments)));
};
}
return iterate([]);
}

function curry(f) { if (f.length == 0) return f; function iterate(args) { if (args.length >= f.length) return f.apply(null, args); return function () { return iterate(args.concat(Array.prototype.slice.call(arguments))); }; } return iterate([]); } function mean3(a, b, c) { return (a + b + c) / 3; } var curriedMean3 = curry(mean3); alert(curriedMean3(1)(2, 3)); // => 2 alert(curriedMean3(1)(2)(3));//空括弧無效 alert(curriedMean3()(1)()(2)()(3)); // => 2 alert(curriedMean3(1, 2)(3, 4)); // => 2 (第四個參數無效)
[Ctrl+A 全選 注:如需引入外部Js需重新整理才能執行]

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.