高级高阶函数:函数的元编程艺术
超越基础的高阶函数
如果说基础的高阶函数如 map、filter、reduce 是函数式编程的工具箱, 那么高级高阶函数就是构建工具的工具——它们不仅处理数据, 更是处理函数本身。
在 JavaScript 的世界里, 函数是一等公民。这意味着函数不仅可以作为参数传递、作为返回值, 还可以被存储、修改、组合。高级高阶函数正是充分利用这一特性, 创造出强大而优雅的抽象。
函数装饰器
装饰器(Decorator)是一种特殊的高阶函数, 它接收一个函数并返回一个增强版本的函数, 而不改变原函数的核心行为。
1. 日志装饰器
javascript
// 基础日志装饰器
function withLogging(fn, label = fn.name) {
return function (...args) {
console.log(`[${label}] 调用, 参数:`, args);
const result = fn(...args);
console.log(`[${label}] 返回:`, result);
return result;
};
}
// 使用装饰器
function add(a, b) {
return a + b;
}
const addWithLogging = withLogging(add, "ADD");
addWithLogging(5, 3);
// [ADD] 调用, 参数: [5, 3]
// [ADD] 返回: 8
// => 82. 性能计时装饰器
javascript
function withTiming(fn, label = fn.name) {
return function (...args) {
const start = performance.now();
const result = fn(...args);
const end = performance.now();
console.log(`[${label}] 执行时间: ${(end - start).toFixed(2)}ms`);
return result;
};
}
// 测量函数性能
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
const timedFib = withTiming(fibonacci, "Fibonacci");
console.log(timedFib(30));
// [Fibonacci] 执行时间: 12.34ms
// => 8320403. 错误处理装饰器
javascript
function withErrorHandling(fn, onError = console.error) {
return function (...args) {
try {
return fn(...args);
} catch (error) {
onError(`函数 ${fn.name} 执行出错:`, error.message);
return null;
}
};
}
// 使用示例
function parseJSON(jsonString) {
return JSON.parse(jsonString);
}
const safeParseJSON = withErrorHandling(parseJSON);
console.log(safeParseJSON('{"name":"John"}')); // { name: 'John' }
console.log(safeParseJSON("{invalid}")); // null (错误被捕获)4. 重试装饰器
javascript
function withRetry(fn, maxAttempts = 3, delay = 1000) {
return async function (...args) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn(...args);
} catch (error) {
if (attempt === maxAttempts) {
throw error;
}
console.log(`尝试 ${attempt} 失败, ${delay}ms 后重试...`);
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
};
}
// 模拟不稳定的 API 调用
async function fetchData(url) {
if (Math.random() < 0.7) {
// 70% 失败率
throw new Error("网络错误");
}
return { data: `来自 ${url} 的数据` };
}
const reliableFetch = withRetry(fetchData, 3, 500);
reliableFetch("https://api.example.com/data")
.then((result) => console.log("成功:", result))
.catch((error) => console.log("最终失败:", error.message));5. 组合多个装饰器
javascript
// 组合装饰器
function compose(...decorators) {
return function (fn) {
return decorators.reduceRight((decorated, decorator) => {
return decorator(decorated);
}, fn);
};
}
// 创建一个完全装饰的函数
function expensiveCalculation(n) {
let result = 0;
for (let i = 0; i < n; i++) {
result += Math.sqrt(i);
}
return result;
}
const fullyDecorated = compose(
withLogging,
withTiming,
withErrorHandling
)(expensiveCalculation);
fullyDecorated(1000000);
// [expensiveCalculation] 调用, 参数: [1000000]
// [expensiveCalculation] 执行时间: 25.67ms
// [expensiveCalculation] 返回: 666666166.6667偏函数应用
偏函数应用(Partial Application)是预先填充函数的部分参数, 创建一个参数更少的新函数。
1. 基础偏函数
javascript
// 通用的偏函数应用
function partial(fn, ...presetArgs) {
return function (...laterArgs) {
return fn(...presetArgs, ...laterArgs);
};
}
// 示例: 数学运算
function multiply(a, b, c) {
return a * b * c;
}
const multiplyByTwo = partial(multiply, 2);
console.log(multiplyByTwo(3, 4)); // 2 * 3 * 4 = 24
const multiplyByTwoAndThree = partial(multiply, 2, 3);
console.log(multiplyByTwoAndThree(4)); // 2 * 3 * 4 = 242. 实际应用: HTTP 请求
javascript
// 通用的 fetch 封装
async function request(baseURL, headers, method, endpoint, body) {
const url = `${baseURL}${endpoint}`;
const options = {
method,
headers: { ...headers, "Content-Type": "application/json" },
...(body && { body: JSON.stringify(body) }),
};
const response = await fetch(url, options);
return response.json();
}
// 创建 API 客户端
const apiRequest = partial(request, "https://api.example.com", {
Authorization: "Bearer token123",
});
const get = partial(apiRequest, "GET");
const post = partial(apiRequest, "POST");
const put = partial(apiRequest, "PUT");
const del = partial(apiRequest, "DELETE");
// 使用
get("/users"); // GET /users
post("/users", { name: "John" }); // POST /users
put("/users/1", { name: "Jane" }); // PUT /users/1
del("/users/1"); // DELETE /users/13. 配置式偏函数
javascript
// 数据验证器
function createValidator(rules, errorMessages, data) {
const errors = [];
for (const [field, rule] of Object.entries(rules)) {
if (!rule(data[field])) {
errors.push({
field,
message: errorMessages[field] || `${field} 验证失败`,
});
}
}
return {
isValid: errors.length === 0,
errors,
};
}
// 预设验证规则和错误信息
const validateUser = partial(
createValidator,
{
name: (value) => value && value.length >= 2,
email: (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
age: (value) => value >= 18 && value <= 100,
},
{
name: "姓名至少需要2个字符",
email: "请输入有效的邮箱地址",
age: "年龄必须在 18-100 之间",
}
);
// 使用
console.log(validateUser({ name: "J", email: "invalid", age: 15 }));
// { isValid: false, errors: [...] }
console.log(
validateUser({
name: "John",
email: "[email protected]",
age: 25,
})
);
// { isValid: true, errors: [] }函数组合器
组合器(Combinator)是操作函数的高阶函数, 用于构建复杂的数据处理管道。
1. Compose 和 Pipe
javascript
// compose: 从右到左执行
const compose =
(...fns) =>
(value) =>
fns.reduceRight((acc, fn) => fn(acc), value);
// pipe: 从左到右执行
const pipe =
(...fns) =>
(value) =>
fns.reduce((acc, fn) => fn(acc), value);
// 数据转换函数
const trim = (str) => str.trim();
const toLowerCase = (str) => str.toLowerCase();
const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
const addExclamation = (str) => str + "!";
// 使用 pipe (更直观)
const formatGreeting = pipe(trim, toLowerCase, capitalize, addExclamation);
console.log(formatGreeting(" HELLO ")); // "Hello!"2. 异步组合
javascript
// 异步 pipe
const asyncPipe =
(...fns) =>
(value) =>
fns.reduce(async (acc, fn) => fn(await acc), value);
// 异步操作
const fetchUser = async (id) => {
await new Promise((resolve) => setTimeout(resolve, 100));
return { id, name: "John", age: 25 };
};
const enrichWithPosts = async (user) => {
await new Promise((resolve) => setTimeout(resolve, 100));
return { ...user, posts: ["Post 1", "Post 2"] };
};
const addTimestamp = async (user) => {
return { ...user, fetchedAt: new Date() };
};
// 组合异步操作
const getUserWithData = asyncPipe(fetchUser, enrichWithPosts, addTimestamp);
getUserWithData(1).then(console.log);
// { id: 1, name: 'John', age: 25, posts: [...], fetchedAt: ... }3. 条件组合
javascript
// 条件执行组合器
const when = (predicate, fn) => (value) => predicate(value) ? fn(value) : value;
const unless = (predicate, fn) => when((value) => !predicate(value), fn);
// 使用示例
const processNumber = pipe(
when((n) => n < 0, Math.abs), // 转为正数
when(
(n) => n > 100,
(n) => 100
), // 限制最大值
unless(Number.isInteger, Math.floor) // 取整
);
console.log(processNumber(-50)); // 50
console.log(processNumber(150)); // 100
console.log(processNumber(45.7)); // 45
console.log(processNumber(30)); // 304. Map 组合器
javascript
// map 组合器 - 将函数应用到数组的每个元素
const map = (fn) => (array) => array.map(fn);
// 组合多个映射操作
const processUsers = pipe(
map((user) => ({ ...user, name: user.name.toUpperCase() })),
map((user) => ({ ...user, isAdult: user.age >= 18 })),
map((user) => ({
...user,
category: user.age < 18 ? "youth" : user.age < 60 ? "adult" : "senior",
}))
);
const users = [
{ name: "john", age: 15 },
{ name: "jane", age: 25 },
{ name: "bob", age: 65 },
];
console.log(processUsers(users));记忆化装饰器
记忆化(Memoization)通过缓存函数结果来优化性能:
javascript
function memoize(fn) {
const cache = new Map();
return function (...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
console.log("从缓存返回:", key);
return cache.get(key);
}
console.log("计算新结果:", key);
const result = fn(...args);
cache.set(key, result);
return result;
};
}
// 斐波那契数列 - 未优化
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
// 记忆化版本
const memoizedFib = memoize(fibonacci);
console.time("First call");
console.log(memoizedFib(40));
console.timeEnd("First call");
console.time("Second call");
console.log(memoizedFib(40));
console.timeEnd("Second call");
// Second call 几乎瞬间完成高级记忆化 - 支持自定义缓存策略:
javascript
function memoizeAdvanced(fn, options = {}) {
const {
maxSize = Infinity,
maxAge = Infinity,
keyGenerator = JSON.stringify,
} = options;
const cache = new Map();
const timestamps = new Map();
return function (...args) {
const key = keyGenerator(args);
const now = Date.now();
// 检查缓存
if (cache.has(key)) {
const timestamp = timestamps.get(key);
// 检查是否过期
if (now - timestamp < maxAge) {
return cache.get(key);
}
// 过期则删除
cache.delete(key);
timestamps.delete(key);
}
// 计算结果
const result = fn(...args);
// 检查缓存大小限制
if (cache.size >= maxSize) {
// 删除最早的条目
const firstKey = cache.keys().next().value;
cache.delete(firstKey);
timestamps.delete(firstKey);
}
// 存入缓存
cache.set(key, result);
timestamps.set(key, now);
return result;
};
}
// 使用示例: API 调用缓存
const fetchUserData = memoizeAdvanced(
async (userId) => {
const response = await fetch(`/api/users/${userId}`);
return response.json();
},
{
maxSize: 100, // 最多缓存 100 个用户
maxAge: 60000, // 缓存 1 分钟
keyGenerator: ([userId]) => `user_${userId}`,
}
);函数工厂
函数工厂是返回配置化函数的高阶函数:
javascript
// 创建验证器工厂
function createValidator(type) {
const validators = {
email: (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
phone: (value) => /^\d{10,11}$/.test(value),
url: (value) => /^https?:\/\/.+/.test(value),
required: (value) => value != null && value !== "",
minLength: (min) => (value) => value.length >= min,
maxLength: (max) => (value) => value.length <= max,
range: (min, max) => (value) => value >= min && value <= max,
};
return validators[type];
}
// 使用
const isEmail = createValidator("email");
const isPhone = createValidator("phone");
const isMinLength8 = createValidator("minLength")(8);
console.log(isEmail("[email protected]")); // true
console.log(isPhone("1234567890")); // true
console.log(isMinLength8("password123")); // true创建数据转换器工厂:
javascript
function createTransformer(type) {
const transformers = {
trim: (str) => str.trim(),
uppercase: (str) => str.toUpperCase(),
lowercase: (str) => str.toLowerCase(),
capitalize: (str) => str.charAt(0).toUpperCase() + str.slice(1),
slugify: (str) =>
str
.toLowerCase()
.replace(/[^\w\s-]/g, "")
.replace(/\s+/g, "-"),
truncate: (maxLength) => (str) =>
str.length > maxLength ? str.slice(0, maxLength) + "..." : str,
padStart:
(length, char = " ") =>
(str) =>
str.padStart(length, char),
padEnd:
(length, char = " ") =>
(str) =>
str.padEnd(length, char),
};
return transformers[type];
}
// 组合转换器
const processTitle = pipe(
createTransformer("trim"),
createTransformer("capitalize"),
createTransformer("truncate")(50)
);
console.log(
processTitle(" this is a very long title that needs processing ")
);实战应用: 中间件系统
javascript
// Express 风格的中间件系统
class MiddlewareEngine {
constructor() {
this.middlewares = [];
}
use(fn) {
this.middlewares.push(fn);
return this;
}
async execute(context) {
let index = 0;
const next = async () => {
if (index >= this.middlewares.length) {
return;
}
const middleware = this.middlewares[index++];
await middleware(context, next);
};
await next();
return context;
}
}
// 使用示例
const app = new MiddlewareEngine();
// 日志中间件
app.use(async (ctx, next) => {
console.log("-> 请求开始:", ctx.path);
await next();
console.log("<- 请求结束:", ctx.path);
});
// 认证中间件
app.use(async (ctx, next) => {
if (!ctx.user) {
ctx.error = "未认证";
return;
}
await next();
});
// 业务逻辑
app.use(async (ctx, next) => {
ctx.result = `处理 ${ctx.path}`;
await next();
});
// 执行
app
.execute({
path: "/api/users",
user: { id: 1, name: "John" },
})
.then((result) => {
console.log("最终结果:", result);
});小结
高级高阶函数是函数式编程中的强大工具, 它们让我们能够:
- 装饰器模式: 增强函数功能而不修改原函数
- 偏函数应用: 创建参数预设的函数变体
- 函数组合: 构建复杂的数据处理管道
- 记忆化: 通过缓存优化性能
- 函数工厂: 生成配置化的函数
这些技术不仅让代码更简洁优雅, 还提供了强大的抽象能力。掌握高级高阶函数, 能让你用更少的代码完成更多的工作, 写出更易维护和测试的程序。
高阶函数的关键是将函数视为数据——它们可以被创建、传递、组合和转换。这种思维方式会彻底改变你构建程序的方式。