函数组合是一种组合两个或多个函数以生成新函数的技术。这个想法是获取一个函数的输出并将其用作另一个函数的输入。
在数学上,给定两个函数f
和g
,它们的组合表示为f(g(x))
。这里,g(x)
首先计算 ,并将其结果传递给f
。
const f = x => x + 2;
const g = x => x * 3;
// f、g进行组合
const composedFunction = x => f(g(x)); // f(g(x)) = f(3x) = 3x + 2
console.log(composedFunction(2));
让我们将函数组合与制作三明治联系起来,这是我们大多数人都熟悉的现实世界的例子。
制作三明治的每个步骤都可以表示为一个函数。当你组合这些函数时,你会得到一个制作三明治的组合函数。
让我们将三明治类比转化为 JavaScript
函数。
// 切片
const sliceBread = bread => `${bread} is sliced`;
// 涂抹
const spreadButter = bread => `Butter spread on ${bread}`;
// 添加材料
const addFilling = bread => `Filling added to ${bread}`;
const makeSandwich = bread => addFilling(spreadButter(sliceBread(bread)));
console.log(makeSandwich("meat"));
函数组合的主要好处之一是可重用性。创建的每个功能都可以独立使用或与其他功能结合使用。如果只想更改某个方法,那么其他的方法都是可以复用的。
const makeToast = bread => spreadButter(sliceBread(bread));
console.log(makeToast("White Bread"));
在 JavaScript
中,接受其他函数作为参数或返回函数的函数称为高阶函数。它们是功能组合的基础。
// 组合两个函数
const compose = (f, g) => x => f(g(x));
const makeSandwich = compose(addFilling, compose(spreadButter, sliceBread));
console.log(makeSandwich("meat"));
JavaScript
中的闭包允许函数从封闭范围访问变量,即使在外部函数完成执行之后也是如此。这个概念在函数组合中特别有用,可以维护不同函数调用之间的状态。
const addTopping = topping => bread => `${topping} added to ${bread}`;
const addLettuce = addTopping("Lettuce");
console.log(addLettuce("Butter spread on Whole Wheat is sliced"));
通过了解前面的内容,我们已经了解了函数组合的基本内容。不过我们在开发的时候可以选择一些三方库来更好的完善。比如Ramda或Lodash/fp等库。这些库提供了一套实用函数,使 JavaScript
中的函数式编程和函数组合更易于访问和管理。
Ramda
示例:
import R from 'ramda';
const sliceBread = bread => `${bread} is sliced`;
const spreadButter = bread => `Butter spread on ${bread}`;
const addFilling = bread => `Filling added to ${bread}`;
const makeSandwich = R.compose(addFilling, spreadButter, sliceBread);
console.log(makeSandwich("Sourdough")); // "Filling added to Butter spread on Sourdough is sliced"
TypeScript
是 JavaScript
的超集,它带来了静态类型。在 TypeScrip
t 中使用函数组合时,类型添加了额外的安全层,确保组合链中的每个函数都遵守特定的约定。
以下是在 TypeScript
中定义compose
函数的示例:
function compose<A, B, C>(f: (b: B) => C, g: (a: A) => B): (a: A) => C {
return (x: A) => f(g(x));
}
const sliceBread = (bread: string) => `${bread} is sliced`;
const spreadButter = (bread: string) => `Butter spread on ${bread}`;
const addFilling = (bread: string) => `Filling added to ${bread}`;
const makeSandwich = compose(addFilling, compose(spreadButter, sliceBread));
console.log(makeSandwich("Multigrain")); // "Filling added to Butter spread on Multigrain is sliced"
隐式编程强调定义函数而不明确提及它们的参数。在 JavaScript
中,高阶函数(将其他函数作为参数或返回它们的函数)在实现这一目标方面发挥着重要作用。
正常编程:
const double = x => x * 2;
const increment = x => x + 1;
const transform = x => increment(double(x));
隐式编程
const double = x => x * 2;
const increment = x => x + 1;
const compose = (f, g) => x => f(g(x));
const transform = compose(increment, double);
在这里,compose
函数是主要参与者,允许我们链接increment
和double并且无需显式引用它们的参数。当我们调用transform
时,它会通过double
和increment
两个函数处理输入,并且不会明确详细说明数据如何在这些函数之间流动。
好处: