高阶函数是指满足以下条件之一的函数:
// 接收函数作为参数
function execute(fn, value) {
return fn(value);
}
// 返回函数
function createMultiplier(factor) {
return function(number) {
return number * factor;
};
}
// 两者兼备
function compose(f, g) {
return function(x) {
return f(g(x));
};
}
// 不使用高阶函数
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);
// 好处:
// 1. 代码更简洁
// 2. 更易复用
// 3. 更易测试
// 4. 声明式编程
将函数作为参数传递是高阶函数的核心用法。
// 回调函数模式
function processData(data, callback) {
const result = data.map(callback);
return result;
}
const numbers = [1, 2, 3, 4, 5];
const doubled = processData(numbers, n => n * 2);
const squared = processData(numbers, n => n * n);
console.log(doubled); // [2, 4, 6, 8, 10]
console.log(squared); // [1, 4, 9, 16, 25]
const users = [
{ name: "东巴文", age: 1 },
{ name: "JavaScript", age: 27 },
{ name: "TypeScript", age: 10 }
];
// filter:过滤
const adults = users.filter(user => user.age >= 18);
// map:映射
const names = users.map(user => user.name);
// find:查找
const found = users.find(user => user.name === "东巴文");
// some:存在判断
const hasAdult = users.some(user => user.age >= 18);
// every:全部判断
const allNamed = users.every(user => user.name);
// sort:排序
const sorted = [...users].sort((a, b) => a.age - b.age);
// reduce:归约
const totalAge = users.reduce((sum, user) => sum + user.age, 0);
// 条件过滤
function filterBy(array, predicate) {
const result = [];
for (let item of array) {
if (predicate(item)) {
result.push(item);
}
}
return result;
}
const numbers = [1, 2, 3, 4, 5, 6];
const evens = filterBy(numbers, n => n % 2 === 0);
console.log(evens); // [2, 4, 6]
// 条件查找
function findBy(array, predicate) {
for (let item of array) {
if (predicate(item)) {
return item;
}
}
return undefined;
}
// 条件分组
function groupBy(array, keyFn) {
return array.reduce((groups, item) => {
const key = keyFn(item);
if (!groups[key]) {
groups[key] = [];
}
groups[key].push(item);
return groups;
}, {});
}
const users = [
{ name: "东巴文", role: "admin" },
{ name: "张三", role: "user" },
{ name: "李四", role: "user" }
];
const grouped = groupBy(users, u => u.role);
// { admin: [...], user: [...] }
返回函数可以创建可配置的函数。
// 创建验证器
function createValidator(rules) {
return function(value) {
for (let rule of rules) {
const error = rule(value);
if (error) return error;
}
return null;
};
}
const validatePassword = createValidator([
v => v.length < 8 ? "密码至少8位" : null,
v => !/[A-Z]/.test(v) ? "需要大写字母" : null,
v => !/[0-9]/.test(v) ? "需要数字" : null
]);
console.log(validatePassword("abc")); // "密码至少8位"
console.log(validatePassword("abcdefgh")); // "需要大写字母"
console.log(validatePassword("Abcdefgh")); // "需要数字"
console.log(validatePassword("Abcdefg1")); // null
// 创建API请求函数
function createApiClient(baseUrl) {
return function(endpoint, options = {}) {
return fetch(`${baseUrl}${endpoint}`, {
...options,
headers: {
"Content-Type": "application/json",
...options.headers
}
}).then(res => res.json());
};
}
const api = createApiClient("https://api.example.com");
api("/users"); // GET https://api.example.com/users
api("/users", { method: "POST", body: JSON.stringify({ name: "东巴文" }) });
// 延迟调用
function delay(fn, ms) {
return function(...args) {
return new Promise(resolve => {
setTimeout(() => {
resolve(fn.apply(this, args));
}, ms);
});
};
}
const delayedLog = delay(console.log, 1000);
delayedLog("东巴文"); // 1秒后输出
// 条件执行
function when(condition, fn) {
return function(...args) {
if (condition(...args)) {
return fn.apply(this, args);
}
};
}
柯里化是将多参数函数转换为一系列单参数函数的技术。
// 普通函数
function add(a, b, c) {
return a + b + c;
}
// 柯里化后
function curriedAdd(a) {
return function(b) {
return function(c) {
return a + b + c;
};
};
}
console.log(add(1, 2, 3)); // 6
console.log(curriedAdd(1)(2)(3)); // 6
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
}
return function(...moreArgs) {
return curried.apply(this, args.concat(moreArgs));
};
};
}
// 使用
const add = (a, b, c) => a + b + c;
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
console.log(curriedAdd(1, 2, 3)); // 6
// 日志函数
const log = curry((level, message) => {
console.log(`[${level}] ${message}`);
});
const info = log("INFO");
const error = log("ERROR");
info("用户登录"); // [INFO] 用户登录
error("系统错误"); // [ERROR] 系统错误
// 过滤函数
const filter = curry((predicate, array) => {
return array.filter(predicate);
});
const filterEvens = filter(n => n % 2 === 0);
const filterPositives = filter(n => n > 0);
console.log(filterEvens([1, 2, 3, 4, 5, 6])); // [2, 4, 6]
// 映射函数
const map = curry((fn, array) => array.map(fn));
const double = map(n => n * 2);
console.log(double([1, 2, 3])); // [2, 4, 6]
偏函数是固定函数的部分参数,返回一个新函数。
// 使用bind创建偏函数
function greet(greeting, name) {
console.log(`${greeting}, ${name}!`);
}
const sayHello = greet.bind(null, "你好");
sayHello("东巴文"); // 你好, 东巴文!
function partial(fn, ...presetArgs) {
return function(...laterArgs) {
return fn.apply(this, [...presetArgs, ...laterArgs]);
};
}
function multiply(a, b, c) {
return a * b * c;
}
const double = partial(multiply, 2);
console.log(double(3, 4)); // 24
const doubleAndTriple = partial(multiply, 2, 3);
console.log(doubleAndTriple(4)); // 24
const _ = Symbol("placeholder");
function partialRight(fn, ...presetArgs) {
return function(...laterArgs) {
const args = [];
let presetIndex = 0;
let laterIndex = 0;
for (let i = 0; i < fn.length; i++) {
if (presetArgs[i] === _) {
args.push(laterArgs[laterIndex++]);
} else {
args.push(presetArgs[presetIndex++]);
}
}
return fn.apply(this, args);
};
}
function greet(greeting, punctuation, name) {
return `${greeting}, ${name}${punctuation}`;
}
const sayHello = partialRight(greet, "你好", _);
console.log(sayHello("东巴文")); // 你好, 东巴文!
函数组合是将多个函数组合成一个新函数。
// 两个函数组合
function compose(f, g) {
return function(x) {
return f(g(x));
};
}
const double = x => x * 2;
const addOne = x => x + 1;
const doubleThenAdd = compose(addOne, double);
console.log(doubleThenAdd(3)); // 7 (3 * 2 + 1)
function compose(...fns) {
return function(x) {
return fns.reduceRight((acc, fn) => fn(acc), x);
};
}
const double = x => x * 2;
const addOne = x => x + 1;
const square = x => x * x;
const calculate = compose(double, addOne, square);
// 执行顺序:square -> addOne -> double
console.log(calculate(3)); // 20 ((3 * 3) + 1) * 2
function pipe(...fns) {
return function(x) {
return fns.reduce((acc, fn) => fn(acc), x);
};
}
const double = x => x * 2;
const addOne = x => x + 1;
const square = x => x * x;
const calculate = pipe(double, addOne, square);
// 执行顺序:double -> addOne -> square
console.log(calculate(3)); // 49 ((3 * 2) + 1) ^ 2
// 数据处理管道
const data = [
{ name: "东巴文", score: 85 },
{ name: "张三", score: 60 },
{ name: "李四", score: 90 },
{ name: "王五", score: 75 }
];
const process = pipe(
arr => arr.filter(item => item.score >= 70),
arr => arr.sort((a, b) => b.score - a.score),
arr => arr.map(item => item.name)
);
console.log(process(data)); // ["李四", "东巴文", "王五"]
记忆化是缓存函数结果以避免重复计算的技术。
function memoize(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
// 不使用记忆化
function fib(n) {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
}
// fib(40) 非常慢
// 使用记忆化
const fastFib = memoize(function fib(n) {
if (n <= 1) return n;
return fastFib(n - 1) + fastFib(n - 2);
});
// fastFib(40) 瞬间完成
// 缓存API请求
const memoizedFetch = memoize(async function(url) {
const response = await fetch(url);
return response.json();
});
// 第一次请求会发送网络请求
memoizedFetch("/api/users");
// 第二次直接返回缓存结果
memoizedFetch("/api/users");
function memoizeWithExpiry(fn, expiryMs) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
const cached = cache.get(key);
if (cached && Date.now() - cached.time < expiryMs) {
return cached.value;
}
const result = fn.apply(this, args);
cache.set(key, { value: result, time: Date.now() });
return result;
};
}
掌握了高阶函数后,让我们继续学习:
东巴文(db-w.cn) - 让编程学习更简单
🎼 东巴文寄语:高阶函数是函数式编程的核心,掌握高阶函数能让你的代码更加简洁、可复用、易测试。柯里化、组合、记忆化都是强大的工具。在 db-w.cn,我们帮你掌握函数式编程的精髓!