深入理解JavaScript函数模式:回调、配置对象与柯里化实战

作者:纸飞机时间:2026-10-29 06:30:21浏览:253

本文深入解析JavaScript中的核心函数模式。通过回调函数实现异步处理,利用配置对象简化参数传递,借助返回函数构建状态计数器。重点讲解偏应用与柯里化的原理及通用实现,展示如何将多参数函数转化为单参数链式调用,提升代码复用性与灵活性。

引言

引言

本文主要探讨JavaScript中Function模式的高级应用技巧(上篇)。利用Function的特性,开发者可以构建出极具创意且高效的代码结构。本文将重点涵盖回调模式、配置对象模式、返回函数、偏应用(Partial Application)以及柯里化(Currying)这五大核心主题。

回调函数模式

回调函数

在JavaScript中,当函数A被作为另一个函数B的参数传入时,函数A即被称为回调函数。这意味着函数A可以在函数B的执行周期内的任意阶段(开始、中间或结束时)被调用执行。

例如,假设存在一个用于生成节点对象的函数。

相关代码如下:

var complexComputation = function () { /* 内部处理,并返回一个node*/};

接着定义一个名为findNodes的函数,用于查找所有节点,并通过回调函数执行后续逻辑。


相关代码如下:

var findNodes = function (callback) {
var nodes = [];
var node = complexComputation();
// 如果回调函数可用,则执行它
if (typeof callback === "function") {
callback(node);
}
nodes.push(node);
return nodes;
};

关于回调函数的定义,我们可以预先定义好一个处理函数来复用:

相关代码如下:

// 定义callback
var hide = function (node) {
node.style.display = "none";
};
// 查找node,然后隐藏所有的node
var hiddenNodes = findNodes(hide);

此外,也可以直接在调用时通过匿名函数定义回调,示例如下:

相关代码如下:

// 使用匿名函数定义callback
var blockNodes = findNodes(function (node) {
node.style.display = 'block';
});

日常开发中使用频率最高的场景之一,莫过于jQuery的ajax方法调用。通过在done或fail回调中定义处理逻辑,可以在ajax请求成功或失败时执行进一步操作。以下代码基于jQuery 1.8版本:


相关代码如下:

var menuId = $("ul.nav").first().attr("id");
var request = $.ajax({
url: "script.php",
type: "POST",
data: {id : menuId},
dataType: "html"
});
//调用成功时的回调处理
request.done(function(msg) {
 $("#log").html( msg );
});
//调用失败时的回调处理
request.fail(function(jqXHR, textStatus) {
 alert( "Request failed: " + textStatus );
});

配置对象模式

配置对象

如果一个函数(或方法)仅接收一个参数,且该参数为对象字面量,这种模式被称为配置对象模式。例如以下代码:

相关代码如下:

var conf = {
username:"shichuan",
first:"Chuan",
last:"Shi"
};
addPerson(conf);

在addPerson函数内部,可以灵活使用conf对象的属性值,这通常用于初始化工作。例如jQuery中的ajaxSetup方法正是采用这种方式实现:


相关代码如下:

// 事先设置好初始值
$.ajaxSetup({
url: "/xmlhttp/",
global: false,
type: "POST"
 });
// 然后再调用
 $.ajax({ data: myData });

此外,许多jQuery插件也采用这种传参形式。当未传递参数时,函数将使用默认值。

返回函数与闭包

返回函数

返回函数模式指的是一个函数的返回值是另一个函数,或者根据特定条件动态创建的新函数。示例代码如下:

相关代码如下:

var setup = function () {
console.log(1);
return function () {
console.log(2);
    };
};
// 调用setup 函数
var my = setup(); // 输出 1
my(); // 输出 2
// 或者直接调用也可
setup()();

利用闭包特性,可以在setup函数内部维护一个私有计数器,每次调用时递增计数器。代码如下:


相关代码如下:

var setup = function () {
var count = 0;
return function () {
return ++count;
    };
};
// 用法
var next = setup();
next(); // 返回 1
next(); // 返回 2
next(); // 返回 3

偏应用函数详解

偏应用

偏应用(Partial Application)是指将参数传入的过程分离。在某些场景下,一系列操作可能始终包含相同的一个或几个参数。此时,我们可以先定义一个偏函数,随后再执行该函数(传入剩余的不同参数)。

举例如下:

相关代码如下:

var partialAny = (function (aps) {
// 该函数是自执行函数表达式的结果,并赋值给了partialAny变量
 function func(fn) {
 var argsOrig = aps.call(arguments, 1);
 return function () {
 var args = [],
 argsPartial = aps.call(arguments),
 i = 0;
// 遍历所有原始参数集,
 // 如果参数是partialAny._ 占位符,则使用下一个函数参数对应的值
 // 否则使用原始参数里的值
 for (; i < argsOrig.length; i++) {
 args[i] = argsOrig[i] === func._
 ? argsPartial.shift()
 : argsOrig[i];
 }
// 如果有额外参数,则添加到尾部
 return fn.apply(this, args.concat(argsPartial));
 };
 }
// 用于占位符设置
 func._ = {};
return func;
})(Array.prototype.slice);

使用方式如下:

相关代码如下:

// 定义处理函数
function hex(r, g, b) {
return '#' + r + g + b;
}
//定义偏函数, 将hex的第一个参数r作为不变的参数值ff
var redMax = partialAny(hex, 'ff', partialAny._, partialAny._);
// 新函数redMax的调用方式如下,只需传入2个参数:
console.log(redMax('11', '22')); // "#ff1122"

如果觉得partialAny._过长,可以使用__代替。


相关代码如下:

var __ = partialAny._;
var greenMax = partialAny(hex, __, 'ff');
console.log(greenMax('33', '44'));
var blueMax = partialAny(hex, __, __, 'ff');
console.log(blueMax('55', '66'));
var magentaMax = partialAny(hex, 'ff', __, 'ff');
console.log(magentaMax('77'));

这样使用,代码更加简洁。

柯里化与通用实现

柯里化

柯里化(Currying)是函数式编程的重要特性,它将多个参数的处理转化为单个参数的处理,类似于链式调用。

以一个简单的add函数为例:

相关代码如下:

function add(x, y) {
var oldx = x, oldy = y;
if (typeof oldy === "undefined") { // partial
return function (newy) {
return oldx + newy;
        }
    }
return x + y;
}

这样调用方式变得多样化,例如:


相关代码如下:

// 测试
typeof add(5); // "function"
add(3)(4); // 7
// 也可以这样调用
var add2000 = add(2000);
add2000(10); // 2010

接下来,我们定义一个通用的currying函数:


相关代码如下:

// 第一个参数为要应用的function,第二个参数是需要传入的最少参数个数
function curry(func, minArgs) {
if (minArgs == undefined) {
minArgs = 1;
    }
function funcWithArgsFrozen(frozenargs) {
 return function () {
 // 优化处理,如果调用时没有参数,返回该函数本身
 var args = Array.prototype.slice.call(arguments);
 var newArgs = frozenargs.concat(args);
 if (newArgs.length >= minArgs) {
 return func.apply(this, newArgs);
 } else {
 return funcWithArgsFrozen(newArgs);
 }
 };
 }
return funcWithArgsFrozen([]);
}

借助此工具,我们可以灵活定义业务逻辑,例如定义加法:

相关代码如下:

var plus = curry(function () {
var result = 0;
for (var i = 0; i < arguments.length; ++i) {
result += arguments[i];
    }
return result;
}, 2);

使用方式多种多样:


相关代码如下:

plus(3, 2) // 正常调用
plus(3) // 偏应用,返回一个函数(返回值为3+参数值)
plus(3)(2) // 完整应用(返回5)
plus()(3)()()(2) // 返回 5
plus(3, 2, 4, 5) // 可以接收多个参数
plus(3)(2, 3, 5) // 同理

以下是减法的示例:


相关代码如下:

var minus = curry(function (x) {
var result = x;
for (var i = 1; i < arguments.length; ++i) {
result -= arguments[i];
    }
return result;
}, 2);

若想交换参数顺序,可以这样定义:


相关代码如下:

var flip = curry(function (func) {
return curry(function (a, b) {
return func(b, a);
}, 2);
});
});

总结

总结

JavaScript中的Function具备诸多特殊功效,利用闭包及arguments参数特性可实现多种技巧。下一篇我们将继续介绍利用Function进行初始化的技巧。

作者最新文章