在JavaScript世界里,你可能会听到的一个术语是 "高阶函数"。今天,我们将探讨高阶函数的含义,并看一看JavaScript中的一些例子。
A 定义
根据定义,高阶函数是一个以一个函数为参数或返回一个函数的函数。
如果你不熟悉把函数当作第一类对象的做法[1],你可能会惊讶于这是可能的。但它确实是这样的,而且它的功能非常强大
一些简单的例子
让我们看几个简单的例子:一个是以一个函数为参数的函数,另一个是返回一个函数。
以一个函数作为参数
让我们创建一个相对无用的函数evaluatesToFive ,它需要两个参数:第一个参数将是一个数字,第二个参数将是一个函数。在我们的evaluatesToFive 函数中,我们将检查传递给函数的数字是否被评估为5。
function evaluatesToFive(num, fn) {
return fn(num) === 5;
}
我们可以在行动中检查它。
function divideByTwo(num) {
return num / 2;
}
evaluatesToFive(10, divideByTwo);
// true
evaluatesToFive(20, divideByTwo);
// false
有点无用,但我们能做到这一点是很好的!
返回一个函数
在我们的下一个例子中,我们将创建一个返回函数的函数。我们创建的函数将被称为multiplyBy 。它将接受一个数字作为参数,并返回一个新的函数,使其输入值乘以该数字。
function multiplyBy(num1) {
return function (num2) {
return num1 * num2;
};
}
现在,我们将通过创建几个乘法函数来看看它的使用情况。
const multiplyByThree = multiplyBy(3);
const multiplyByFive = multiplyBy(5);
multipyByThree(10); // 30
multiplyByFive(10); // 50
同样,在目前的形式下,它并不是超级有用的,但无论如何都是非常酷的。
一个更复杂且可能有用的例子
高阶函数的一个更有用的例子是一个对象验证器。其基本思想是一个函数,它以一个对象为参数,然后是任何数量的函数,这些函数必须评估为true ,才能认为该对象有效。
在这个例子中,我们将处理一个newUser 对象,并试图确定我们是否应该允许他们注册我们的应用程序。该用户必须满足以下条件。
- 必须至少年满18岁
- 密码必须至少有8个字符
- 必须同意服务条款
一个理想的newUser 对象应该是这样的。
const newUser = {
age: 24,
password: 'some long password',
agreeToTerms: true,
};
基于这些知识,我们可以创建一些测试函数,当我们所需的条件得到满足时返回true ,否则返回false 。
function oldEnough(user) {
return user.age >= 18;
}
function passwordLongEnough(user) {
return user.password.length >= 8;
}
function agreeToTerms(user) {
return user.agreeToTerms === true;
}
现在,我们可以创建一个接受任何数量参数的函数。第一个参数将是我们要验证的对象,其余的参数将是用来测试我们对象的测试函数。
function validate(obj, ...tests) {
for (let i = 0; i < tests.length; i++) {
if (tests[i](obj) === false) {
return false;
}
}
return true;
}
那么,这里究竟发生了什么?这里有一个演练。
- 我们指定函数的第一个参数是一个对象(
obj)。然后,我们使用休息运算符(...tests),表示任何其他参数都将在tests数组中。 - 我们使用一个
for循环来遍历我们的tests数组,它是一个函数数组(这是高阶部分!)。 - 我们把
obj传给tests数组中的每一项。如果该函数的值是false,我们就知道obj是无效的,并立即返回false。 - 如果我们通过了整个
tests数组而没有返回false,我们的对象是有效的,我们返回true。
在行动中看到它
现在我们通过验证几个潜在的新用户对象来使用我们的validate高阶函数。
const newUser1 = {
age: 40,
password: 'tncy4ty49r2mrx',
agreeToTerms: true,
};
validate(newUser1, oldEnough, passwordLongEnough, agreeToTerms);
// true
const newUser2 = {
age: 40,
password: 'short',
agreeToTerms: true,
};
validate(newUser2, oldEnough, passwordLongEnough, agreeToTerms);
// false
我们有了!newUser1 被正确地认为是有效的,但是newUser2 被检测到是无效的,因为它的password 太短。
一个潜在的改进:一个创建验证器的函数
奖励点:如果我们将我们的validate 函数应用于多个用户,可能是一个更好的主意,不必重复地指定相同的测试,一遍又一遍。相反,我们可以有一个createValidator 函数来返回一个对象验证器。在这种情况下,我们将创建一个userValidator ,对我们试图验证的任何用户应用相同的测试功能。
function createValidator(...tests) {
return function (obj) {
for (let i = 0; i < tests.length; i++) {
if (tests[i](obj) === false) {
return false;
}
}
return true;
};
}
让我们看看,当我们再次验证我们的newUser1 和newUser2 对象时,这给了我们一个更一致的界面。
const userValidator = createValidator(
oldEnough,
passwordLongEnough,
agreeToTerms
);
userValidator(newUser1); // true
userValidator(newUser2); // false
真棒!通过采用我们的createValidator 高阶函数,我们不可能意外地对我们不同的对象使用不同的验证标准。