发布于 2026-01-05 0 阅读
0

JavaScript 中的高阶函数 (HoF) - 用通俗易懂的方式解释(适合五岁儿童)

JavaScript 中的高阶函数 (HoF) - 用通俗易懂的方式解释(适合五岁儿童)

JavaScript 函数

函数是许多编程语言不可或缺的一部分,JavaScript 也不例外。在 JavaScript 中,functions函数是一等公民。你可以创建函数、给函数赋值、将函数作为参数传递给其他函数,也可以从函数中返回函数值。

这些灵活性有助于代码重用、编写简洁的代码以及实现代码组合。今天我们将学习如何Higher-Order Functions在 JavaScript 中充分利用函数。

如果您也喜欢通过视频学习,本文也有视频教程版本,点击此处观看:🙂

别忘了订阅,以便获取更多内容。

什么是高阶函数?

AHigher-Order Function是一个常规函数,它接受一个或多个函数作为参数,并且/或者返回一个函数作为其值。

以下是一个以函数作为参数的函数示例。

// Define a function that takes a function as an argument.
function getCapture(camera) {
  // Invoke the passed function
  camera();
}

// Invoke the function by passing a function as an argument
getCapture(function(){
  console.log('Canon');
});
Enter fullscreen mode Exit fullscreen mode

现在让我们来看另一个返回函数的函数。

// Define a function that returns a function
function returnFunc() {
  return function() {
    console.log('Hi');
  }
}

// Take the returned function in a variable.
const fn = returnFunc();
// Now invoke the returned function.
fn(); // logs 'Hi' in the console

// Alternatively - A bit odd syntax but good to know
returnFunc()(); // logs 'Hi' in the console
Enter fullscreen mode Exit fullscreen mode

以上两个例子都是高阶函数的例子。函数 `f`getCapture()returnFunc()`g` 都是高阶函数。它们要么接受一个函数作为参数,要么返回一个函数。

请注意,函数并非必须Higher-Order function同时接受参数和返回函数。执行其中任何一项操作都会使该函数成为高阶函数。

为什么要使用高阶函数?如何创建高阶函数?

所以,我们已经了解了什么是高阶函数。现在,让我们来了解一下为什么需要高阶函数以及如何创建它?让我们通过几个简单的例子来说明。

问题:代码污染和代码异味

我们取一个数字数组,

const data = [12, 3, 50];
Enter fullscreen mode Exit fullscreen mode

现在我们来编写代码,将数组中的每个元素递增一个数字,并返回修改后的数组。你可以考虑将其编写成一个函数。

function incrArr(arr, n) {
  let result = [];

  // Iterate through each elements and
  // add the number
  for (const elem of arr) {
    result.push(elem + n);
  }

  return result;
}
Enter fullscreen mode Exit fullscreen mode

所以,如果我们这样做的话,

incrArr(data, 2);
Enter fullscreen mode Exit fullscreen mode

输出,

[14, 5, 52]
Enter fullscreen mode Exit fullscreen mode

目前为止很棒。现在,如果我让你编写代码,将数组中的每个元素减去data一个数字,并返回修改后的数组呢?你可能会想到几种直接的解决方法。首先,你可以编写一个类似这样的函数:

function decrArr(arr, n) {
  let result = [];

  for (const elem of arr) {
    result.push(elem - n);
  }

  return result;
}
Enter fullscreen mode Exit fullscreen mode

但这样代码重复太多了。我们几乎incrArr()decrArr()函数内部编写了函数中的每一行代码。所以,我们来考虑一下代码的复用性。

现在,您可能希望优化代码,使其能够用一个单独的函数有条件地执行这两个操作。

function doOperations(arr, n, op) {
  let result = [];

  for (const elem of arr) {
    if (op === 'incr') {
      result.push(elem + n);  
    } else if (op === 'decr') {
      result.push(elem - n);
    }
  }

  return result;
}
Enter fullscreen mode Exit fullscreen mode

所以,现在我们需要第三个参数来决定操作是增加还是减少数组的元素。但这也有问题。如果我让你把数组中的每个元素乘以一个数呢?你可能会想到else-ifdoOperations()函数里再加一个参数。但这并不好。

每增加一项新操作,都需要修改核心函数的逻辑。这会导致函数代码污染,并增加出错的概率code smells。让我们使用Higher-Order函数来解决这个问题。

解决方案:高阶函数

首先要做的是创建用于递增和递减操作的纯函数。这些函数一次只能执行一项操作。

// Increment the number by another number
function incr(num, pad) {
  return num + pad;
}

// Decrement the number by another number
function decr(num, pad) {
  return num - pad;
}
Enter fullscreen mode Exit fullscreen mode

接下来,我们将编写Higher-Order function一个接受函数作为参数的函数。在这种情况下,传入的函数将是上面定义的纯函数之一。

function smartOperation(data, operation, pad) {
  // Check is the passed value(pad) is not a number.
  // If so, handle it by assigning to the zero value.
  pad = isNaN(pad) ? 0 : pad;

  let result = [];
  for (const elem of data) {
    result.push(operation(elem, pad));
  }
  return result;
}
Enter fullscreen mode Exit fullscreen mode

请仔细观察上面的函数。第一个参数是要操作的数组。第二个参数是操作本身,这里我们直接传递了函数。最后一个参数是要增加或减少的数字。

现在,让我们调用该函数将数组元素加 3。

const data = [12, 3, 50];
const result = smartOperation(data, incr, 3);
console.log(result);
Enter fullscreen mode Exit fullscreen mode

输出,

[15, 6, 53]
Enter fullscreen mode Exit fullscreen mode

现在试试递减操作怎么样?

const data = [12, 3, 50];
const result = smartOperation(data, decr, 2);
console.log(result);
Enter fullscreen mode Exit fullscreen mode

输出,

[10, 1, 48]
Enter fullscreen mode Exit fullscreen mode

你有没有注意到,这次我们并没有为了适应新的运算而对函数做任何修改?这就是使用高阶函数的好处。你的代码干净整洁,没有代码异味。那么,现在我们该如何实现乘法运算呢?很简单,我们来看看。

首先,创建一个执行乘法运算的函数。

function mul(num, pad) {
  return num * pad;
}
Enter fullscreen mode Exit fullscreen mode

接下来,通过传递乘法运算函数来调用高阶函数mul()

const data = [12, 3, 50];
const result = smartOperation(data, mul, 3);
console.log(result);
Enter fullscreen mode Exit fullscreen mode

输出,

[36, 9, 150]
Enter fullscreen mode Exit fullscreen mode

太棒了!万岁Higher-Order functions

JavaScript 中的内置高阶函数

在 JavaScript 中,高阶函数有很多用途。你可能在不知不觉中使用了它们,却不知道它们是高阶函数。

例如,常见的数组方法有 `__getitem__` map()、 `__getitem__` filter()reduce()`__getitem__` find()、`__getitem__` 等等。所有这些函数都接受另一个函数作为参数,并将其应用于数组的元素。

下面这个示例展示了filter()如何根据我们作为函数参数传递给它的条件来过滤数组元素。

const data = [1, 23, 45, 67, 8, 90, 43];

const result = data.filter(function(num){
    return (num % 2 === 0);
});

console.log(result); // [8, 90]
Enter fullscreen mode Exit fullscreen mode

高阶函数与回调函数

高阶函数和回调函数之间总是存在一些混淆。高阶函数(HoF)和回调函数(CB)是不同的。

  • 高阶函数(HoF):接受一个或多个其他函数作为参数和/或返回一个函数作为值的函数。
  • 回调函数(CB):传递给另一个函数的函数。

结论

总之,Higher-Order function高阶函数是 JavaScript 语言中一个基础概念。我们需要在编码实践中尽可能地利用它。高阶函数与纯函数结合使用,可以帮助你保持代码的简洁性和无副作用。

最后,我将分享一篇关于 JavaScript 的文章Pure FunctionSide Effects希望您阅读愉快。

JavaScript 中的纯函数和副作用是什么?

您可以在这篇 StackBlitz 项目中找到文章中使用的所有源代码。

https://stackblitz.com/edit/learn-js-hof



希望这篇文章对您有所帮助。感谢阅读。请点赞/分享,让更多人看到。

让我们保持联系。我也会在这些平台上分享我在JavaScript、Web开发、职业发展和内容创作方面的学习心得。

文章来源:https://dev.to/atapas/higher-order-functionshof-in-javascript-explain-like-im- Five-402f