前言
柯里化
柯里化(Currying),又称为部分求值(Partial Evaluation),是函数式编程中的一种技术。它的核心思想是将原本接受多个参数的函数转换为一系列单参数函数的嵌套调用。
具体来说,假设有一个函数 f(a, b, c)
,经过柯里化之后,可以转换为 f(a)(b)(c)
。这样的转换带来了几个好处:
- 提高函数的复用性:可以通过提前固定一部分参数来创建新的函数,这样就减少了代码重复,增强了代码的灵活性和模块化。
- 函数组合:柯里化使得函数更容易进行组合,可以通过简单地将一个函数的输出作为另一个函数的输入来构建复杂的操作,提高代码的可读性和可维护性。
- 延迟计算:因为参数是逐个提供的,可以在需要时才计算最终结果,这对于控制计算流程和优化性能非常有用,尤其是在处理昂贵的运算或者条件执行逻辑时。
正文
两数相加,接受两个参数
两数相加,用之前的思维来就是函数接受两个参数,最后返回两数相加。
如下所示,这里面还增加了两个判断语句。使用arguments对象检查传入的参数数量是否少于2,如果参数不足,打印提示信息并直接返回,避免后续代码执行;检查传入参数a和b的数据类型是否都是数字。
js
function add(a, b){
if (arguments.length < 2){
console.log('参数数量不够哦');
return;
}
// 数据类型 参数数量的问题
if (typeof a != 'number' || typeof b != 'number'){
console.log('类型错误');
return;
}
return a + b;
}
console.log(add(2, 3));
结合闭包实现两数相加,一次只接收一个参数
add函数返回了一个内部函数,这个内部函数记住了它被创建时的外部环境中的变量a,即使在add函数执行完毕后,这个内部函数依然能够访问到a的值。
当我们两次调用add函数时(第一次传入2,第二次通过返回的函数传入4),实际上利用了闭包的特性来累加这两个数值并最终输出结果6。
js
// add2 = add(2) 返回的仍然是一个函数
// add2(4) = 6
function add(a){
// 第一个参数a
return function(b){
// 这里的a是外部add函数的参数,对于这个内部函数来说,a就是一个自由变量
return a + b;
}
}
console.log(add(2)(4));
参数不定时,柯里化结合闭包
思路:
- 首先定义一个函数curry,接收两个参数fn和...args,fn是需要被柯里化的函数,...args是当前收集的参数。
- 原函数add是需要被柯里化的函数,在这个函数中接收4个参数
- 在curry函数中判断当前收集的参数数量
args.length
是否等于原函数fn需要的参数数量fn.length
。 - 如果收集的参数足够,也就是
args.length >= fn.length
,那么就使用rest运算符展开args,直接调用原函数add并返回结果。 - 如果收集的参数还不够,也就是
args.length < fn.length
,那么就返回一个新的函数curry(fn, ...args, ..._args)
,这个函数继续收集剩下的参数_args
。
js
// 只有一句代码,是返回值本身
const curry = (fn, ...args) =>
args.length >= fn.length ? fn(...args) : (..._args) => curry(fn, ...args, ..._args)
// 原函数add
// 柯里化 慢慢收集
const add = (x, y, z, m) => {
return x + y + z + m;
}
console.log(curry(add, 2)(2)(3, 4)); // 输出结果是11,相当于调用了add(2, 2, 3, 4)
使用curry函数对add函数进行柯里化处理,先传入第一个参数2,然后逐步传递剩余参数,每次调用返回一个新的函数等待下一个参数
这段代码首先定义了一个curry
函数,它负责逐步收集参数并判断是否已收集齐原函数所需的全部参数。如果收集齐了,就执行原函数;如果没有,就继续返回一个函数等待更多的参数。之后,通过curry
函数将一个多参数的add
函数转换成可以逐个传参的形式,并通过一系列函数调用计算出了最终结果11。
小知识点
在JavaScript中,rest运算符和扩展运算符虽然都用三个点(...)表示,但它们在用途上有明显的区别:
-
Rest运算符(...):
- 主要用于收集函数调用时传入的多余参数,将这些参数放入一个数组中。
- 在数组解构赋值中,也可以用来捕获数组中剩余的元素,将它们组合成一个新的数组。
示例:
js
function sum(...numbers) {
let total = 0;
numbers.forEach(num => total += num);
return total;
}
sum(1, 2, 3, 4); // 返回10
// 解构赋值中的Rest
const [a, b, ...others] = [1, 2, 3, 4, 5];
console.log(others); // 输出 [3, 4, 5]
-
扩展运算符(...):
- 扩展运算符可以简化数组操作,比如合并数组、复制数组或对象,以及将数组元素作为函数参数传递。
示例:
javascript// 合并数组 const arr1 = [1, 2, 3]; const arr2 = [4, 5, 6]; const combined = [...arr1, ...arr2]; // 结果是 [1, 2, 3, 4, 5, 6] // 作为函数参数传递 function concat(a, b, c) { console.log(a, b, c); } const args = [1, 2, 3]; concat(...args); // 输出 1 2 3
总结来说,rest运算符主要用于收集多个项为一个数组,而扩展运算符则是将数组拆分为多个单项或合并数组和对象。两者虽符号相同,但在具体应用中功能恰好互补。
结语
本文深入探讨了函数式编程中的核心概念------柯里化(Currying)及其在JavaScript中的应用,希望可以给你带来帮助。