JavaScript中认识什么是高阶函数?

89 阅读4分钟

在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;
}

那么,这里究竟发生了什么?这里有一个演练。

  1. 我们指定函数的第一个参数是一个对象(obj)。然后,我们使用休息运算符(...tests),表示任何其他参数都将在tests 数组中。
  2. 我们使用一个for 循环来遍历我们的tests 数组,它是一个函数数组(这是高阶部分!)。
  3. 我们把obj 传给tests 数组中的每一项。如果该函数的值是false ,我们就知道obj 是无效的,并立即返回false
  4. 如果我们通过了整个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;
  };
}

让我们看看,当我们再次验证我们的newUser1newUser2 对象时,这给了我们一个更一致的界面。

const userValidator = createValidator(
  oldEnough,
  passwordLongEnough,
  agreeToTerms
);

userValidator(newUser1); // true
userValidator(newUser2); // false

真棒!通过采用我们的createValidator 高阶函数,我们不可能意外地对我们不同的对象使用不同的验证标准。