部分应用是函数式编程里的常见操作,核心逻辑是固定一个函数的部分参数,生成一个接收剩余参数的新函数,减少重复传参的同时提升代码复用性。JavaScript的闭包特性可以完美支撑这种实现,因为闭包能保存外层函数的作用域变量,让固定下来的参数在后续调用时依然可用。

核心概念铺垫
什么是闭包
闭包指的是函数可以访问其词法作用域之外的变量,即使外层函数已经执行完毕,这些变量依然会被保留。简单来说,当一个函数内部返回另一个函数,内部函数引用了外层函数的变量,就形成了闭包。
什么是部分应用
部分应用和柯里化容易混淆,柯里化是把多参数函数转成一系列单参数函数,而部分应用是固定任意数量的参数,返回接收剩余参数的函数。比如一个接收三个参数的函数,固定前两个参数,返回的函数只需要接收第三个参数即可执行。
基础实现:固定单个参数
我们先从一个简单的加法函数入手,实现固定其中一个参数的部分应用。假设我们有一个计算两个数之和的函数:
function add(a, b) {
return a + b;
}
现在我们想固定第一个参数为5,生成一个加5的函数,用闭包实现如下:
function partialAdd(a) {
// 闭包保存了传入的a的值
return function(b) {
return add(a, b);
};
}
// 生成固定第一个参数为5的新函数
const addFive = partialAdd(5);
// 调用新函数,只需要传剩余参数
console.log(addFive(3)); // 输出8
console.log(addFive(10)); // 输出15
这里partialAdd执行后返回的内部函数形成了闭包,保存了传入的a的值,后续调用addFive的时候,依然能拿到之前传入的5,完成加法运算。
通用实现:支持任意参数固定
上面的例子只能固定第一个参数,实际场景中我们可能需要固定任意位置的参数,或者固定多个参数。我们可以实现一个通用的部分应用函数,接收原始函数和部分参数,返回接收剩余参数的新函数。
/**
* 通用部分应用实现
* @param {Function} fn 原始函数
* @param {...any} fixedArgs 要固定的参数
* @returns {Function} 接收剩余参数的新函数
*/
function partial(fn, ...fixedArgs) {
return function(...remainingArgs) {
// 合并固定参数和剩余参数,按顺序传入原始函数
return fn.apply(this, fixedArgs.concat(remainingArgs));
};
}
这个实现的核心是利用闭包保存传入的fn和fixedArgs,返回的内部函数接收剩余参数后,把两部分参数合并传给原始函数执行。我们来看几个使用场景:
场景1:固定前两个参数
function multiply(a, b, c) {
return a * b * c;
}
// 固定前两个参数为2和3
const multiplyTwoAndThree = partial(multiply, 2, 3);
console.log(multiplyTwoAndThree(4)); // 2*3*4=24
console.log(multiplyTwoAndThree(5)); // 2*3*5=30
场景2:固定中间参数
如果需要固定中间位置的参数,我们可以用undefined占位,调用的时候再传入对应位置的参数:
// 固定第二个参数为10,第一个和第三个参数后续传入 const partialMultiply = partial(multiply, undefined, 10); // 第一个参数传2,剩余参数传3,对应第三个参数 console.log(partialMultiply(2, 3)); // 2*10*3=60
实际应用示例
部分应用在实际开发中非常实用,比如处理事件监听的时候固定部分参数,或者处理接口请求的时候固定基础配置。
事件监听场景
假设我们有一个处理点击的函数,需要传入事件对象和额外的配置参数:
function handleClick(config, event) {
console.log('配置:', config);
console.log('点击事件:', event);
}
// 固定配置参数
const handleClickWithConfig = partial(handleClick, { theme: 'dark', lang: 'zh' });
// 给按钮添加点击事件,只需要传事件对象
document.getElementById('btn').addEventListener('click', handleClickWithConfig);
接口请求场景
封装请求函数的时候,固定基础路径和请求头:
function request(baseUrl, headers, url, data) {
return fetch(baseUrl + url, {
method: 'POST',
headers: headers,
body: JSON.stringify(data)
}).then(res => res.json());
}
// 固定基础路径和请求头
const apiRequest = partial(request, 'https://ipipp.com/api', {
'Content-Type': 'application/json',
'Token': 'test_token'
});
// 调用的时候只需要传接口路径和参数
apiRequest('/user/list', { page: 1 }).then(data => console.log(data));
注意事项
- 闭包会保存外层变量,如果固定的参数占用内存较大,或者部分应用生成的函数长期不被释放,可能会造成内存占用过高,需要根据场景合理使用。
- 通用实现中参数合并是按顺序拼接的,如果需要更复杂的参数占位逻辑,可以在通用函数里增加占位符判断,比如用
partial函数接收特殊占位符,在合并参数的时候替换成剩余参数。 - 部分应用生成的新函数的
this指向可以通过apply或者call调整,上面的通用实现里用了apply把内部函数的this传给原始函数,避免this指向丢失的问题。
JavaScript闭包部分应用函数式编程修改时间:2026-07-13 16:06:45