你通常认为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) 的内置高阶方法?请在下面的评论中写下你的猜测!