JavaScript Currying 函数的演进与实现详解

作者:拾光时间:2026-10-26 17:18:46浏览:177

本文深入解析 JavaScript 中 Currying(柯里化)函数的历史演变与多种实现方式。从早期基于 arguments 的多态分支处理,到利用正则表达式与 eval 的复杂尝试,再到现代基于闭包和数组拼接的灵活实现。文章详细展示了如何处理不定参数、支持多次调用累积参数以及自动判断执行时机的优化方案,帮助开发者掌握高阶函数在 JS 中的核心应用。

JavaScript Currying 函数的演进与实现详解

早期的 Currying 函数实现带有一定的多态特征,其核心逻辑是根据传入参数的数量在内部选择不同的执行分支。


具体代码实现如下所示:

 
//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 来构建一个更接近现代柯里化概念的函数。


相关代码如下:

 
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;
} 

  

[Ctrl+A 全选 注:引入外部Js需再刷新一下页面才能执行]


现代 Currying 函数的基础实现

随着闭包概念的普及以及将 arguments 转换为数组技术的成熟,现代 Currying 函数才真正得以应用。这如同 15 至 17 世纪的大航海时代,极大地拓展了 JavaScript 的开发视野。


一个典型的现代 Currying 函数示例如下:

 
//一个简单的现代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());

支持多次传入多个参数的优化方案

或者,我们可以每次传入多个参数:


代码如下:

 
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());

自动判断执行时机的最终改进

上述函数存在一个不足:最后必须显式添加括号才能触发执行。如果我们希望只要参数足够就自动返回结果,而忽略多余参数,可以进行如下改进:


代码如下:

 
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([]);
} 

  

[Ctrl+A 全选 注:引入外部Js需再刷新一下页面才能执行]

作者最新文章