什么是JavaScript中的高阶函数?

179 阅读5分钟

你通常认为JavaScript函数是可重复使用的代码片断,可以进行一些计算。参数是函数的输入数据,而返回值是输出。

下面是一个简单的函数,对一个数组进行求和。

js

function calculate(numbers) {
  let sum = 0;
  for (const number of numbers) {
    sum = sum + number;
  }
  return sum;
}
calculate([1, 2, 4]); // => 7

数字是输入,而函数calculate() ,返回总和--输出。

如果实现一个更通用的函数,支持对数字的更多操作:加法、乘法等等。你会如何实现呢?

让我们看看高阶函数的概念是什么,以及它如何使calculate() 函数在支持的操作方面更加通用。

1.高阶函数

让我们停顿一下,思考一下基本原理。

在JavaScript中,函数可以使用原始类型(如数字、字符串)、对象(如数组、纯对象、正则表达式等)作为参数,并且也可以返回原始类型或对象。

在前面的例子中,calculate([1, 2, 4]) 接受一个数字数组作为参数,并返回数字7 --总和。

但是,是否可以将函数作为值使用呢?把函数本身分配给变量,把它们作为参数使用,甚至返回?是的,这是有可能的!

这都是因为JavaScript中的函数是一流的公民。这意味着你可以

A.将函数分配给变量。

javascript

// Assign to variables
const hiFunction = function() { 
  return 'Hello!' 
};
hiFunction(); // => 'Hello!'

B.使用函数作为其他函数的参数。

javascript

// Use as arguments
function iUseFunction(func) {
  return func();
}
iUseFunction(function () { return 42 }); // => 42

C.甚至还可以从函数中返回函数。

javascript

// Return function from function
function iReturnFunction() {
  return function() { return 42 };
}
const myFunc = iReturnFunction();
myFunc(); // => 42

最后,这里是有趣的部分:

那些使用其他函数作为参数或返回函数的函数被命名为高阶函数

在前面的例子中,iUseFunction() 是高阶函数,因为它接受了一个函数作为参数。同样,iReturnFunction() 也是一个高阶函数,因为它返回另一个函数。

另一方面,只使用基元或对象作为参数,并且只返回基元或对象的函数被称为一阶函数

在前面的例子中,hiFunction() 是一个一阶函数,因为它只是返回一个数字。

所以,在JavaScript中*,一个函数可以是一阶的,也可以是高阶的。*

这很有趣,但为什么高阶函数是有用的呢?接下来让我们来了解一下!

2.实践中的高阶函数

让我们回顾一下帖子介绍中的问题。如何使calculate() 函数支持对数组的多种操作?

答案是让calculate() 成为一个高阶函数,并以函数的形式动态地提供操作作为参数。

让我们修改这个函数来实现它。

javascript

function calculate(operation, initialValue, numbers) {
  let total = initialValue;
  for (const number of numbers) {
    total = operation(total, number);
  }
  return total;
}
function sum(n1, n2) {
  return n1 + n2;
}
function multiply(n1, n2) {
  return n1 * n2;
}
calculate(sum, 0, [1, 2, 4]);      // => 7
calculate(multiply, 1, [1, 2, 4]); // => 8

以下是变化的内容:calculate(operation, initialValue, numbers) 接受的第一个参数是描述操作的函数,第二个参数是初始值,最后第三个参数是数字数组。

现在calculate(operation, initialValue, numbers) 是一个高阶函数,因为它接受了一个函数作为第一个参数。

sum() 是描述加法运算的函数。 是用这个函数来执行数字的和。calculate(sum, 0, [1, 2, 4])

同样,multiply() 描述了乘法运算。calculate(multiply, 1, [1, 2, 4]) 正在使用multiply() 函数来执行所有数字的生产。

另外,在调用calculate(sum, 0, [1, 2, 4]) ,第一个参数也被称为回调函数。你可以认为一个高阶函数接受或返回回调函数。

3.高阶函数的好处

最棒的是你可以重用 calculate() ,通过提供不同的操作函数来支持多种操作:加法、乘法,等等。

此外,高阶函数的概念允许函数的可组合性。例如,你将calculate()sum() 进行组合,计算一个数组中所有数字的总和。如果你想计算产量,那么你可以将calculate()multiply()

由于高阶函数的可组合性是函数式编程的一个重要工具。

高阶函数有助于减少代码的重复,有利于单一责任原则

4.高阶函数的例子

如果你仔细观察一下关于数组、字符串、DOM方法、承诺方法的内置JavaScript函数--你可以注意到,其中许多函数只要接受一个函数作为参数,就属于高阶函数。

例如,array.map(mapperFunc) 方法是一个高阶函数,因为它接受了一个映射器函数作为参数。

javascript

const numbers = [1, 2, 4];
const doubles = numbers.map(function mapper(number) {
  return 2 * number;
});
doubles; // [2, 4, 8]

element.addEventListener(type, handler) DOM方法也是一个高阶函数,因为它接受了事件处理函数作为第二个参数。

javascript

document
  .getElementById('#myButton')
  .addEventListener('click', function handler() {
    console.log('The button was clicked!');
  });

5.总结

JavaScript中的高阶函数是一类特殊的函数,它要么接受函数作为参数,要么返回函数。

另一方面,如果函数只使用基元或对象作为参数或返回值,这些函数是一阶函数。

高阶函数提供了可重用性的好处:主要的行为是由高阶函数本身提供的,通过接受函数作为参数,你可以随意扩展这个行为。

挑战:数组对象上是否有类似于calculate(operation, initialValue, numbers) 的内置高阶方法?请在下面的评论中写下你的猜测!