高阶函数
约 1627 字大约 5 分钟
2026-04-16
1. 什么是高阶函数
高阶函数(Higher-Order Function)是指满足以下条件之一的函数:
- 函数作为参数传入:将一个函数作为参数传递给另一个函数
- 函数作为返回值:返回一个函数作为结果
高阶函数是函数式编程的核心概念,它让我们能够对基本操作进行抽象和组合。
// 函数作为参数
function execute(fn, x) {
return fn(x);
}
// 函数作为返回值
function createMultiplier(multiplier) {
return function(x) {
return x * multiplier;
};
}2. 常见的高阶函数示例
2.1 Array.map()
map() 创建一个新数组,该数组由原数组中的每个元素调用一个提供的函数后返回的结果组成。
const numbers = [1, 2, 3, 4, 5];
// 基础用法
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// 配合对象使用
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
{ name: 'Charlie', age: 35 }
];
const names = users.map(user => user.name);
console.log(names); // ['Alice', 'Bob', 'Charlie']2.2 Array.filter()
filter() 创建一个新数组,包含所有通过测试的元素。
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 过滤偶数
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4, 6, 8, 10]
// 过滤成年人
const users = [
{ name: 'Alice', age: 17 },
{ name: 'Bob', age: 25 },
{ name: 'Charlie', age: 16 }
];
const adults = users.filter(user => user.age >= 18);
console.log(adults); // [{ name: 'Bob', age: 25 }]2.3 Array.reduce()
reduce() 对数组中的每个元素按序执行一个提供的 reducer 函数,每一次运行 reducer 会将先前元素的计算结果作为参数传入。
const numbers = [1, 2, 3, 4, 5];
// 求和
const sum = numbers.reduce((acc, cur) => acc + cur, 0);
console.log(sum); // 15
// 将数组转成对象
const items = ['a', 'b', 'c', 'd'];
const itemObject = items.reduce((acc, item, index) => {
acc[index] = item;
return acc;
}, {});
console.log(itemObject); // {0: 'a', 1: 'b', 2: 'c', 3: 'd'}
// 二维数组展平
const nested = [[1, 2], [3, 4], [5, 6]];
const flat = nested.reduce((acc, cur) => acc.concat(cur), []);
console.log(flat); // [1, 2, 3, 4, 5, 6]2.4 Array.forEach()
forEach() 对数组的每个元素执行一次提供的函数。
const fruits = ['apple', 'banana', 'cherry'];
fruits.forEach(fruit => {
console.log(fruit);
});
// apple
// banana
// cherry
// 带索引
fruits.forEach((fruit, index) => {
console.log(`${index}: ${fruit}`);
});2.5 Array.sort()
sort() 对数组的元素进行排序,默认按 Unicode 码点顺序排列。传入比较函数可以自定义排序规则。
const numbers = [5, 2, 8, 1, 4];
// 默认排序(字符串比较)
console.log(numbers.sort()); // [1, 2, 4, 5, 8]
// 自定义排序
const nums = [10, 2, 8, 1, 4];
nums.sort((a, b) => a - b);
console.log(nums); // [1, 2, 4, 8, 10]
// 降序
nums.sort((a, b) => b - a);
console.log(nums); // [10, 8, 4, 2, 1]
// 对象数组排序
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
{ name: 'Charlie', age: 20 }
];
users.sort((a, b) => a.age - b.age);
console.log(users);
// [{ name: 'Charlie', age: 20 },
// { name: 'Alice', age: 25 },
// { name: 'Bob', age: 30 }]2.6 setTimeout / setInterval
// setTimeout - 延迟执行
console.log('Start');
setTimeout(() => {
console.log('Delayed execution');
}, 1000);
console.log('End');
// Start
// End
// Delayed execution (1秒后)
// setInterval - 间隔执行
let count = 0;
const intervalId = setInterval(() => {
count++;
console.log(`Count: ${count}`);
if (count >= 3) {
clearInterval(intervalId);
}
}, 1000);
// Count: 1 (1秒后)
// Count: 2 (2秒后)
// Count: 3 (3秒后)3. 手写简易版理解原理
3.1 手写 map
Array.prototype.myMap = function(fn, thisArg) {
const result = [];
for (let i = 0; i < this.length; i++) {
result.push(fn.call(thisArg, this[i], i, this));
}
return result;
};
// 测试
const numbers = [1, 2, 3];
const doubled = numbers.myMap(num => num * 2);
console.log(doubled); // [2, 4, 6]3.2 手写 filter
Array.prototype.myFilter = function(fn, thisArg) {
const result = [];
for (let i = 0; i < this.length; i++) {
if (fn.call(thisArg, this[i], i, this)) {
result.push(this[i]);
}
}
return result;
};
// 测试
const numbers = [1, 2, 3, 4, 5];
const evens = numbers.myFilter(num => num % 2 === 0);
console.log(evens); // [2, 4]3.3 手写 reduce
Array.prototype.myReduce = function(fn, initialValue) {
let accumulator = initialValue !== undefined ? initialValue : this[0];
let startIndex = initialValue !== undefined ? 0 : 1;
for (let i = startIndex; i < this.length; i++) {
accumulator = fn(accumulator, this[i], i, this);
}
return accumulator;
};
// 测试
const numbers = [1, 2, 3, 4, 5];
// 无初始值
const sum = numbers.myReduce((acc, cur) => acc + cur);
console.log(sum); // 15
// 有初始值
const sumWithInit = numbers.myReduce((acc, cur) => acc + cur, 10);
console.log(sumWithInit); // 254. 高阶函数的优势
4.1 抽象
高阶函数让我们能够抽象出通用的操作模式,不必为每种具体情况编写重复的循环逻辑。
// 没有高阶函数 - 手动循环
const numbers = [1, 2, 3, 4, 5];
const doubled = [];
for (let i = 0; i < numbers.length; i++) {
doubled.push(numbers[i] * 2);
}
// 使用高阶函数 - 清晰简洁
const doubled2 = numbers.map(n => n * 2);4.2 复用
封装在函数中的逻辑可以在多处复用了。
// 提取通用的排序逻辑
const sortByAge = (users) => users.sort((a, b) => a.age - b.age);
const sortByName = (users) => users.sort((a, b) => a.name.localeCompare(b.name));
// 数据处理
const processUsers = (users, processor) => processor(users);
processUsers(allUsers, sortByAge);
processUsers(allUsers, sortByName);4.3 组合
多个高阶函数可以组合使用,形成管道式的处理流程。
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const result = numbers
.filter(n => n % 2 === 0) // [2, 4, 6, 8, 10]
.map(n => n * n) // [4, 16, 36, 64, 100]
.reduce((acc, n) => acc + n, 0); // 220
console.log(result); // 2205. 函数柯里化(Currying)
柯里化是将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。
// 普通函数
function add(a, b, c) {
return a + b + c;
}
console.log(add(1, 2, 3)); // 6
// 柯里化版本
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return function(...args2) {
return curried.apply(this, args.concat(args2));
};
}
};
}
const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6
console.log(curriedAdd(1)(2, 3)); // 6
// 柯里化的实际应用
const multiply = curry((a, b, c) => a * b * c);
const multiplyBy2 = multiply(2);
const multiplyBy2And3 = multiplyBy2(3);
const result = multiplyBy2And3(4);
console.log(result); // 24柯里化与高阶函数的关系:柯里化函数返回一个函数(函数作为返回值),因此它是高阶函数的一种应用形式。
6. Compose 与高阶函数
compose 将多个函数组合成一个函数,从右到左依次执行。
// 简单的 compose 实现
function compose(...fns) {
return function(x) {
return fns.reduceRight((acc, fn) => fn(acc), x);
};
}
// 使用 compose
const square = x => x * x;
const double = x => x * 2;
const addOne = x => x + 1;
// compose 从右到左执行
const composed = compose(
square, // 最后执行
double, // 第二个执行
addOne // 第一个执行
);
console.log(composed(2)); // ((2 + 1) * 2)^2 = 36
// 执行过程:addOne(2) = 3 -> double(3) = 6 -> square(6) = 36pipe 与 compose 的区别:
compose:从右到左执行pipe:从左到右执行
function pipe(...fns) {
return function(x) {
return fns.reduce((acc, fn) => fn(acc), x);
};
}
const piped = pipe(
addOne, // 第一个执行
double, // 第二个执行
square // 最后执行
);
console.log(piped(2)); // ((2 + 1) * 2)^2 = 36
// 执行过程相同:addOne(2) = 3 -> double(3) = 6 -> square(6) = 36compose 的实际应用:
// 数据处理管道
const processUser = compose(
validateEmail,
normalizeEmail,
createUser
);
const user = processUser({ email: ' USER@EXAMPLE.COM ' });