JavaScript 函数柯里化与反柯里化详解
本文深入解析JavaScript中的函数柯里化与反柯里化概念。通过ES6语法展示基础与高阶柯里化的通用实现,并探讨反柯里化在方法借用中的应用。文章详细列举了柯里化在参数复用、提高适用性及延迟执行方面的三种核心用法,帮助开发者理解并优化代码结构。
函数柯里化的基本概念
柯里化(Currying)的核心机制是将接收多个参数的原始函数,转换为接收单一参数(即原函数的第一个参数)并返回一个新函数的过程。这个新函数能够继续接收剩余的参数,并最终返回与原函数调用相同的结果。
ES6 实现通用柯里化
接下来探讨如何使用 ES6 语法来实现通用的柯里化逻辑。
function currying(fn,...rest1){
return function(...rest2){
//这里用apply 是为把数组形式的参数直接传入原函数 null是因为不需要改变this
return fn.apply(null,rest1.concat(rest2));
}
}
以 `sayHello` 函数为例,演示具体的柯里化过程。
function sayHello(name,age,fruit){
console.log(`我叫${name},我${age}岁了,我喜欢吃${fruit}`);
}
//传入第一个参数
let curryingShowMsg = currying(sayHello,'小明');
//执行传入剩余参数
curryingShowMsg(22,'芒果');
反柯里化的原理与实现
反柯里化的作用与柯里化相反。为了扩大方法的使用场景,反柯里化技术可以将原生方法“借用”出来,使得任意对象都能拥有并调用原生对象的方法。
这里需要明确区分柯里化与反柯里化的核心区别。
//柯里化
//function(arg1,arg2) => function(arg1)(arg2);
//function(arg1,arg2,...,argn) => function(arg1)(arg2)(...)(argn)
//反柯里化
//obj.func(arg1,arg2) => func(obj,arg1,arg2)
基于 ES6 语法,我们可以实现一个通用的反柯里化函数。
function unCurrying(fn){
//tar 是我们借给的对象 以前需要xxx.fn(xx) 现在就可以fn(xxx,xx)
return function(tar,...argu){
return fn.apply(tar,argu);
}
}
//比如我们想把Array.prototype.push方法从原生借出来
let push = unCurrying(Array.prototype.push);
//arrguments是我们借给的对象
push(arguments,4);
高阶柯里化的通用实现
上述示例仅实现了单层简单的柯里化。若要实现完全的多层柯里化(即 `func(xx)(xx)(xx)(xx)` 的形式),需要反复嵌套柯里化函数。下面提供一个更高阶的通用柯里化实现方案。
function curryingHelper(fn,len){
//这里先说个基本知识点 fn.length 返回的是这个方法需要传入的参数个数
//这里第一次运行时通过fn.length 后面递归都是用的传入的len len为剩余的参数个数
const length = len || fn.length;
return function(...rest){
//判断这次传入的参数是否大于等于剩下的参数 如果不是递归返回下一个方法继续传参
return rest.length >= length
? fn.apply(this,rest)
: curryingHelper(currying.apply(this,[fn].concat(rest)),length-rest.length)
}
}
//还是刚才的sayHello 函数
let betterShowMsg = curryingHelper(sayHello);
//自动控制传参层数
betterShowMsg('zyx')(22)('葡萄');
betterShowMsg('zyx',22)('葡萄');
柯里化的三种核心用法
函数柯里化在实际开发中主要有三种典型的应用场景。
第一种用法是参数的复用。
function Say(name,some){
console.log(name + '说' + some);
}
//如果我们只想让zyx说一些东西
let zyxSay = currying(Say,'zyx');
zyxSay('1111');//zyx说1111
第二种用法是提高代码的适用性。
//通用函数解决了兼容性的问题,但是同时在不同场景下,我们可能同一种规则需要反复使用
//这就可能会造成代码的重复性 比如
function square(i){ return i*i }//平方
function dubble(i){ return i*2 }//双倍
function map(handler,list){
//handle 是操作的规则 list是操作的arrguments
return list.map(handler)
}
map(square,[1,2,3]);//数组每一项平方
map(dubble,[1,2,3]);//数组每一项加倍
//这就是通用性 我可以用同一个函数做很多不同的操作
//但是如果我们需要大量做平方操作 每次我们都需要传入方法再传入数组就造成的代码浪费
//这时我们通过柯里化提高实用性
let mapSQ = currying(map,square);//直接定义出来的新的平凡操作函数
mapSQ([1,2,3]);//以后就不用传入操作方法了
第三种用法是实现延迟执行。
let add = function(...rest){
//定义一个闭包保存_args
const _args = [];
return function cb(...rest){
if(rest.length == 0){
//如果不穿参数了 也就是add() 说明我们需要最后执行函数了
let res = 0;
//累加
console.log(_args);
for(let data of _args){
res += data;
}
return res;
}else{
_args.push(...rest);
//为了锁住args 闭包
return _args;
}
}
}()
add(1);
add(2);
let a = add();
console.log(a);//3
以上是对 JavaScript 函数柯里化的详细解析,如需了解更多相关内容,请持续关注本站的其他文章。
JS创建数组的两种方式:字面量[]与new Array()的区别
2026-11-05 18:02:28
JavaScript异步发送HTTP请求时怎么优雅地处理错误
2026-11-05 17:10:44
5款主流JS在线运行工具对比:CodePen、JSFiddle与StackBlitz怎么选
2026-11-05 17:04:04
JavaScript入门书籍避坑:这些书不适合初学者
2026-11-05 16:31:15
JS使用Math.round实现保留两位小数的原理与代码
2026-11-05 15:35:55
