JavaScript 函数增强之高阶函数
2025-04-12 03:10:18    1.3k 字   
This post is also available in English and alternative languages.

我平时用 Java 开发,发现 JavaScript 的高阶函数(Higher-Order Functions)和 Java 8 的「行为参数化(Behavior parameterization)」有许多相似之处。这篇文章就来探讨高阶函数的概念及其应用。


1. 高阶函数是啥?

高阶函数就是一个”会玩函数的函数”。它有两种”玩法”:

  • 接收函数:把其他函数作为参数传入
  • 返回函数:返回一个新函数作为结果

普通函数就像一个老老实实的工人,只会干一件事。高阶函数则像个”工头”,要么指挥别的工人干活,要么培训一个新工人出来。

想象你在饭店点菜:

  • 普通函数是”厨师”,你说炒个青菜,他就炒青菜。
  • 高阶函数是”老板”,你告诉他”怎么炒”(比如加辣、不加盐),他再指挥厨师按你的想法干。

2. 与Java 8「行为参数化」对比

在 Java 8 中,我们通常使用 Lambda 表达式和流式 API 来实现行为参数化:

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Main {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> evens = numbers.stream()
.filter(num -> num % 2 == 0) // 传个"行为"
.collect(Collectors.toList());
System.out.println(evens); // [2, 4]
}
}

JavaScript 的高阶函数实现类似功能:

1
2
3
const numbers = [1, 2, 3, 4, 5];
const evens = numbers.filter(num => num % 2 === 0); // 传个"行为"
console.log(evens); // [2, 4]
  • 相同点:
    • 都将”行为”作为参数传递(Java 使用 Lambda,JavaScript 使用函数)
    • 都避免了硬编码逻辑,提高了代码复用性和灵活性
    • 都遵循函数式编程的思想
  • 不同点:
    • Java 需要通过函数式接口和类型系统来实现,语法相对复杂一些
    • JavaScript 作为函数式语言,函数是一等公民,可以直接传递函数

本质上,高阶函数和「行为参数化」都是函数式编程思想的体现:将”怎么做”参数化,使代码更加灵活和可复用。


3. 高阶函数的两种类型

3.1. 接收函数作为参数

这种高阶函数将其他函数作为参数,让它们执行特定任务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function filterArray(arr, predicate) { // predicate 是一个函数(行为)
return arr.filter(predicate);
}

const numbers = [1, 2, 3, 4, 5];
const evens = filterArray(numbers, num => num % 2 === 0); // 筛选偶数
console.log(evens); // [2, 4]

const odds = filterArray(numbers, num => num % 2 !== 0); // 筛选奇数
console.log(odds); // [1, 3, 5]

// 更复杂的筛选条件
const greaterThanThree = filterArray(numbers, num => num > 3);
console.log(greaterThanThree); // [4, 5]

filterArray 是高阶函数,它不直接实现筛选逻辑,而是通过 predicate 函数来决定”筛选什么”。这样一个函数就能应对各种筛选需求。


3.2. 返回函数作为结果

这种高阶函数返回一个新函数,像个”工厂”,可以生产定制化的函数:

1
2
3
4
5
6
7
8
9
10
11
12
function createGreeter(greeting) {
return function (name) { // 返回一个新函数
return greeting + "," + name + "!";
};
}

const sayHello = createGreeter("你好"); // 造了个“你好”问候器
console.log(sayHello("小白")); // 你好,小白!
console.log(sayHello("小黑")); // 你好,小黑!

const sayHi = createGreeter("嗨"); // 造了个“嗨”问候器
console.log(sayHi("小白")); // 嗨,小白!

createGreeter 是个”函数工厂”,你给它一个问候词,它就生产一个专用的问候函数。


4. 高阶函数的实际应用

4.1. 自定义排序

1
2
3
4
5
6
7
8
9
10
const students = [
{ name: "张三", score: 85 },
{ name: "李四", score: 92 },
{ name: "王五", score: 78 }
];

// 按分数从高到低排序
students.sort((a, b) => b.score - a.score);
console.log(students);
// 输出: [{ name: "李四", score: 92 }, { name: "张三", score: 85 }, { name: "王五", score: 78 }]

4.2. 函数组合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 函数组合:将多个函数组合成一个函数
function compose(...functions) {
return function(input) {
return functions.reduceRight((result, fn) => fn(result), input);
};
}

// 示例函数
const double = x => x * 2;
const addOne = x => x + 1;
const square = x => x * x;

// 组合成新函数: square(addOne(double(x)))
const transform = compose(square, addOne, double);

console.log(transform(3)); // 49 (计算过程: 3→6→7→49)

5. 小结

使用高阶函数有很多好处:

  1. 代码简洁:减少重复代码,提高可读性
  2. 灵活性:同一个高阶函数可以应对不同场景
  3. 可测试性:更容易单元测试
  4. 可组合性:可以组合多个小函数实现复杂功能
  5. 抽象层次:提高代码的抽象层次,更加专注于”做什么”而非”怎么做”

对比一下不使用高阶函数和使用高阶函数的代码差异:

  • 不适用高阶函数

    1
    2
    3
    4
    5
    6
    7
    8
    const numbers = [1, 2, 3, 4, 5];
    let evens = [];
    for (let i = 0; i < numbers.length; i++) {
    if (numbers[i] % 2 === 0) {
    evens.push(numbers[i]);
    }
    }
    console.log(evens); // [2, 4]
  • 使用高阶函数:

    1
    2
    3
    const numbers = [1, 2, 3, 4, 5];
    const evens = numbers.filter(num => num % 2 === 0);
    console.log(evens); // [2, 4]

高阶函数版本不仅代码更简洁,而且更加声明式,直接表达了”我想要筛选偶数”的意图,而不是陷入具体的实现细节。


高阶函数是”会玩函数的函数”——它能接收函数作为参数或返回函数作为结果。与Java 8的「行为参数化」类似,高阶函数将”怎么做”参数化,使代码更加灵活和可复用。