在之前的两篇文章:
我介绍了JS函数式编程中的一些概念及其实践,本文则着重详细、深入介绍里面最常用的技术之一 —— 函数柯里化。
柯里化(Currying)
柯里化(Currying)是一种关于函数的高阶技术,它允许你将一个有多个参数的函数转化为一连串的函数,它返回一个新的函数,等待下一个参数的内联。换句话说,一个函数不是一次接受所有的参数,而是接受第一个参数并返回一个新的函数,该函数接受第二个参数并返回一个新的函数,该函数接受第三个参数,以此类推,直到所有参数都得到满足。
这个概念是以数学家
Haskell Curry
的名字命名的,他在1930年代提出了这个概念。在JavaScript中,Currying
是一个强大的工具,它可以将一个有多个参数的函数转化为一系列嵌套的函数,每个函数只接受一个参数。Currying
帮助你避免多次传递同一个变量,并帮助你创建一个高阶函数。可以提高代码的可重用性、可组合性和可维护性。
在本文中,我们将深入探讨curry,提供全面的解释和例子。
柯里化有两个特点:
- 柯里化是一种函数的转换,它是指将一个函数从可调用的
f(a, b, c)
转换为可调用的f(a)(b)(c)
。 - 柯里化不会调用函数。它只是对函数进行转换。
先来看一个例子,创建一个辅助函数 curry(f)
,该函数将对两个参数的函数 f
执行柯里化。换句话说,对于两个参数的函数 f(a, b)
执行 curry(f)
会将其转换为以 f(a)(b)
形式运行的函数:
function curry(f) { // curry(f) 执行柯里化转换 return function(a) { return function(b) { return f(a, b); }; }; } // 用法 function sum(a, b) { return a + b; } let curriedSum = curry(sum); alert( curriedSum(1)(2) ); // 3
正如你所看到的,实现非常简单:只有两个包装器(wrapper)。
curry(func)
的结果就是一个包装器function(a)
。- 当它被像
curriedSum(1)
这样调用时,它的参数会被保存在词法环境中,然后返回一个新的包装器function(b)
。 - 然后这个包装器被以
2
为参数调用,并且,它将该调用传递给原始的sum
函数。
柯里化更高级的实现,例如 lodash 库的 _.curry,会返回一个包装器,该包装器允许函数被正常调用或者以部分应用函数(partial)的方式调用:
function sum(a, b) { return a + b; } let curriedSum = _.curry(sum); // 使用来自 lodash 库的 _.curry alert( curriedSum(1, 2) ); // 3,仍可正常调用 alert( curriedSum(1)(2) ); // 3,以部分应用函数的方式调用
函数科里化(Currying)是JavaScript中的一个高级特性,它能够将接受多个参数的函数变成一系列只接受部分参数的函数。这种技术在函数式编程中经常使用,并且可以使代码更加灵活和可复用。以下是一个详细的js代码示例,展示了如何实现函数科里化。
// 定义一个普通的函数,它接收两个参数并返回他们的和 function add(x, y) { return x + y; } // 编写一个curry函数,它可以对任何函数进行科里化 function curry(fn) { // 获取除第一个参数外的其他参数 var args = Array.prototype.slice.call(arguments, 1); // 返回一个新函数,如果参数足够执行则调用原来的函数,否则继续科里化 return function() { var newArgs = args.concat(Array.prototype.slice.call(arguments)); if (newArgs.length >= fn.length) { return fn.apply(this, newArgs); } else { return curry.call(this, fn, ...newArgs); } }; } // 使用curry函数对add进行科里化 var curriedAdd = curry(add); // 调用curriedAdd函数,返回一个新函数 var add1 = curriedAdd(1); // 继续调用新函数,返回结果2 console.log(add1(1)); // 2 // 另一种调用方式 console.log(curriedAdd(2)(3)); // 5
在上面的代码中,我们首先定义了一个普通的函数add
,它接收两个参数并返回他们的和。然后,我们编写了一个名为curry
的函数,它接受一个要进行科里化的函数作为第一个参数,并且通过Array.prototype.slice.call(arguments, 1)
获取除了第一个参数外的其他参数。
在curry
函数内部,我们返回了一个新函数,这个新函数如果接收到的参数足够执行,则调用原来的函数。否则,继续使用curry
函数对其进行科里化。这个过程通过newArgs
变量将之前传入的参数和当前传入的参数合并起来实现。最后,我们通过return curry.call(this, fn, ...newArgs);
递归调用curry
函数,直到参数足够执行时结束递归调用。
我们使用curry
函数对add
函数进行科里化,得到一个名为curriedAdd
的函数。我们可以使用curriedAdd
函数来创建新的函数,例如使用curriedAdd(1)
返回一个只需要一个参数的新函数。当我们调用这个新函数时,它会返回一个结果,而不是立即执行。当我们重新调用这个函数,将它的返回值和另一个参数一起传入时,它会再次返回结果,依此类推。
总之,函数科里化是JavaScript中一个非常强大的技术,它可以使代码更加灵活和可复用。在某些特定场景下,使用函数科里化可以简化代码并提高开发效率。