跳到主要内容

函数式编程速查表

本文档提供了函数式编程核心概念的快速参考,包括常用模式、代码示例和最佳实践。

核心概念速览

概念定义关键特点
纯函数相同输入总是返回相同输出,无副作用可缓存、可测试、可并行
不可变性数据创建后不可修改安全共享、时间旅行调试
高阶函数接受或返回函数的函数抽象、复用、组合
闭包函数与其词法环境的组合数据封装、状态保持
柯里化多参数函数转单参数链延迟计算、参数复用
函数组合将函数输出作为下一个函数输入声明式、可读性强
函子实现了 map 的容器统一转换接口
单子实现了 flatMap 的容器链式操作、展平嵌套
尾调用优化递归时不增加调用栈的优化技术防止栈溢出、内存高效
Lens访问和更新深层数据的抽象不可变更新、可组合

纯函数

判断标准

// ✅ 纯函数
const add = (a, b) => a + b;
const double = x => x * 2;
const formatName = (first, last) => `${first} ${last}`;

// ❌ 非纯函数(依赖外部状态)
let counter = 0;
const increment = () => ++counter;

// ❌ 非纯函数(有副作用)
const saveToDb = data => database.save(data);

// ❌ 非纯函数(修改输入参数)
const addItem = (arr, item) => arr.push(item);

纯函数改造

// 改造前:有副作用
let total = 0;
function addToTotal(value) {
total += value;
return total;
}

// 改造后:纯函数
function addToTotal(total, value) {
return total + value;
}

// 使用 reduce 保持不可变性
const sum = values.reduce((total, value) => total + value, 0);

不可变操作

数组操作

const arr = [1, 2, 3];

// 添加元素
const pushed = [...arr, 4]; // [1, 2, 3, 4]
const unshifted = [0, ...arr]; // [0, 1, 2, 3]

// 删除元素
const withoutFirst = arr.slice(1); // [2, 3]
const withoutLast = arr.slice(0, -1); // [1, 2]
const withoutIndex = (arr, i) => [...arr.slice(0, i), ...arr.slice(i + 1)];
const withoutValue = arr.filter(x => x !== 2); // [1, 3]

// 修改元素
const updated = arr.map((x, i) => i === 1 ? 99 : x); // [1, 99, 3]

// 插入元素
const inserted = (arr, i, val) => [...arr.slice(0, i), val, ...arr.slice(i)];
inserted(arr, 1, 1.5); // [1, 1.5, 2, 3]

// 常用操作
const unique = [...new Set([1, 1, 2, 2, 3])]; // [1, 2, 3]
const reversed = [...arr].reverse(); // [3, 2, 1]
const flattened = [[1, 2], [3, 4]].flat(); // [1, 2, 3, 4]

对象操作

const obj = { a: 1, b: 2, c: 3 };

// 添加/修改属性
const added = { ...obj, d: 4 }; // { a: 1, b: 2, c: 3, d: 4 }
const modified = { ...obj, a: 99 }; // { a: 99, b: 2, c: 3 }

// 删除属性
const { b, ...withoutB } = obj; // withoutB: { a: 1, c: 3 }

// 嵌套对象修改
const nested = { user: { name: '张三', age: 25 } };
const updated = {
...nested,
user: { ...nested.user, age: 26 }
};

// 深层路径更新工具函数
const updateIn = (obj, path, fn) => {
if (path.length === 0) return fn(obj);
const [head, ...tail] = path;
return {
...obj,
[head]: updateIn(obj[head] || {}, tail, fn)
};
};

高阶函数

内置方法

const numbers = [1, 2, 3, 4, 5];

// map: 转换
numbers.map(x => x * 2); // [2, 4, 6, 8, 10]

// filter: 过滤
numbers.filter(x => x % 2 === 0); // [2, 4]

// reduce: 归约
numbers.reduce((acc, x) => acc + x, 0); // 15

// find: 查找
numbers.find(x => x > 3); // 4

// some/every: 判断
numbers.some(x => x > 4); // true
numbers.every(x => x > 0); // true

// flatMap: 映射后展平
[[1, 2], [3, 4]].flatMap(x => x); // [1, 2, 3, 4]

// reduce 常见用法
const sum = arr => arr.reduce((a, b) => a + b, 0);
const max = arr => arr.reduce((a, b) => a > b ? a : b);
const groupBy = key => arr => arr.reduce((acc, item) => {
(acc[item[key]] = acc[item[key]] || []).push(item);
return acc;
}, {});

自定义高阶函数

// 函数工厂
const multiplier = factor => number => number * factor;
const double = multiplier(2);
double(5); // 10

// 记忆化
const memoize = fn => {
const cache = new Map();
return (...args) => {
const key = JSON.stringify(args);
if (!cache.has(key)) cache.set(key, fn(...args));
return cache.get(key);
};
};

// 只执行一次
const once = fn => {
let called = false;
let result;
return (...args) => {
if (!called) {
called = true;
result = fn(...args);
}
return result;
};
};

// 防抖
const debounce = (fn, delay) => {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
};

// 节流
const throttle = (fn, interval) => {
let last = 0;
return (...args) => {
const now = Date.now();
if (now - last >= interval) {
last = now;
return fn(...args);
}
};
};

// 管道包装器
const withLogging = fn => (...args) => {
console.log('调用:', args);
const result = fn(...args);
console.log('结果:', result);
return result;
};

闭包模式

数据封装

const createCounter = (initial = 0) => {
let count = initial;
return {
increment: () => ++count,
decrement: () => --count,
get: () => count,
reset: () => { count = initial; return count; }
};
};

const counter = createCounter(10);
counter.increment(); // 11
counter.get(); // 11

函数配置

const createLogger = (prefix, level = 'INFO') => 
message => console.log(`[${level}] ${prefix}: ${message}`);

const userLog = createLogger('UserService');
userLog('用户登录'); // [INFO] UserService: 用户登录

const errorLog = createLogger('API', 'ERROR');
errorLog('请求失败'); // [ERROR] API: 请求失败

私有变量

const createWallet = (initialBalance) => {
let balance = initialBalance;
let transactions = [];

return {
deposit: (amount) => {
balance += amount;
transactions.push({ type: 'deposit', amount, balance });
return balance;
},
withdraw: (amount) => {
if (amount > balance) throw new Error('余额不足');
balance -= amount;
transactions.push({ type: 'withdraw', amount, balance });
return balance;
},
getBalance: () => balance,
getHistory: () => [...transactions]
};
};

函数组合

compose 与 pipe

// compose: 从右到左
const compose = (...fns) => x =>
fns.reduceRight((v, f) => f(v), x);

// pipe: 从左到右(推荐)
const pipe = (...fns) => x =>
fns.reduce((v, f) => f(v), x);

// 使用示例
const add1 = x => x + 1;
const double = x => x * 2;
const subtract3 = x => x - 3;

const process = pipe(add1, double, subtract3);
process(5); // 9 (5 → 6 → 12 → 9)

实用组合

// 数据处理管道
const getActiveUsers = users => users.filter(u => u.active);
const sortByAge = users => [...users].sort((a, b) => a.age - b.age);
const getNames = users => users.map(u => u.name);

const getActiveUserNames = pipe(getActiveUsers, sortByAge, getNames);

// 字符串处理
const trim = s => s.trim();
const toLower = s => s.toLowerCase();
const slugify = pipe(
trim,
toLower,
s => s.replace(/\s+/g, '-'),
s => s.replace(/[^a-z0-9-]/g, '')
);

// 调试工具
const tap = label => value => {
console.log(`[${label}]:`, value);
return value;
};

const debug = pipe(
tap('输入'),
processData,
tap('处理后'),
formatOutput
);

柯里化

基础柯里化

// 手动柯里化
const add = a => b => c => a + b + c;
add(1)(2)(3); // 6

// 通用柯里化函数
const curry = fn => {
const curried = (...args) =>
args.length >= fn.length
? fn(...args)
: (...more) => curried(...args, ...more);
return curried;
};

const sum = (a, b, c) => a + b + c;
const curriedSum = curry(sum);
curriedSum(1)(2)(3); // 6
curriedSum(1, 2)(3); // 6
curriedSum(1)(2, 3); // 6

常用柯里化模式

// 配置预设
const fetchAPI = baseURL => endpoint => options =>
fetch(`${baseURL}${endpoint}`, options);

const api = fetchAPI('https://api.example.com');
const users = api('/users');
users({ method: 'GET' });

// 过滤器
const filterBy = key => value => arr =>
arr.filter(item => item[key] === value);

const getActive = filterBy('active')(true);
getActive(users);

// 映射
const pluck = key => arr => arr.map(item => item[key]);
const getNames = pluck('name');
getNames(users);

// 条件组合
const when = (pred, fn) => x => pred(x) ? fn(x) : x;
const unless = (pred, fn) => x => !pred(x) ? fn(x) : x;

const doubleIfPositive = when(x => x > 0, x => x * 2);

函子与单子

Maybe 类型

const Maybe = {
of: value => value == null ? Nothing : Just(value),
fromNullable: value => value == null ? Nothing : Just(value)
};

const Just = value => ({
isJust: true,
value,
map: fn => Maybe.of(fn(value)),
flatMap: fn => fn(value),
getOrElse: () => value,
fold: (_, onJust) => onJust(value)
});

const Nothing = {
isJust: false,
map: () => Nothing,
flatMap: () => Nothing,
getOrElse: defaultValue => defaultValue,
fold: onNothing => onNothing()
};

// 使用:安全访问嵌套属性
const safeGet = (...keys) => obj =>
keys.reduce(
(maybe, key) => maybe.flatMap(o => Maybe.of(o?.[key])),
Maybe.of(obj)
);

const user = { profile: { name: '张三' } };
safeGet('profile', 'name')(user).getOrElse('未知'); // '张三'
safeGet('profile', 'age')(user).getOrElse(0); // 0

Either 类型

const Left = value => ({
isLeft: true,
value,
map: () => Left(value),
flatMap: () => Left(value),
fold: (onLeft, _) => onLeft(value)
});

const Right = value => ({
isLeft: false,
value,
map: fn => Right(fn(value)),
flatMap: fn => fn(value),
fold: (_, onRight) => onRight(value)
});

// 使用:携带错误信息
const divide = (a, b) =>
b === 0 ? Left('除数不能为零') : Right(a / b);

divide(10, 2).fold(
err => `错误: ${err}`,
val => `结果: ${val}`
); // '结果: 5'

// 链式验证
const validateEmail = email =>
!email ? Left('邮箱不能为空') :
!email.includes('@') ? Left('邮箱格式不正确') :
Right(email);

Result 类型

const Result = {
ok: value => ({ ok: true, value }),
err: error => ({ ok: false, error }),

fromTry: fn => {
try { return Result.ok(fn()); }
catch (e) { return Result.err(e); }
},

map: (result, fn) =>
result.ok ? Result.fromTry(() => fn(result.value)) : result,

andThen: (result, fn) =>
result.ok ? fn(result.value) : result
};

// 使用
const parseJSON = str => Result.fromTry(() => JSON.parse(str));
const result = parseJSON('{"name":"张三"}');
// { ok: true, value: { name: '张三' } }

惰性求值

生成器基础

// 生成器函数
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
}

const gen = numberGenerator();
gen.next(); // { value: 1, done: false }
gen.next(); // { value: 2, done: false }
gen.next(); // { value: 3, done: false }
gen.next(); // { value: undefined, done: true }

// 无限序列
function* naturals() {
let n = 0;
while (true) {
yield n++;
}
}

// 斐波那契
function* fibonacci() {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}

// 取前 N 个
function take(gen, n) {
const result = [];
for (let i = 0; i < n; i++) {
const { value, done } = gen.next();
if (done) break;
result.push(value);
}
return result;
}

// 使用
const fib = fibonacci();
take(fib, 10); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

惰性序列操作

// 惰性 map
function* lazyMap(iterable, fn) {
for (const item of iterable) {
yield fn(item);
}
}

// 惰性 filter
function* lazyFilter(iterable, predicate) {
for (const item of iterable) {
if (predicate(item)) {
yield item;
}
}
}

// 惰性 take
function* lazyTake(iterable, n) {
let count = 0;
for (const item of iterable) {
if (count >= n) break;
yield item;
count++;
}
}

// 组合使用
const result = [...lazyTake(
lazyFilter(
lazyMap([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], x => x * 2),
x => x > 10
),
3
)];
// [12, 14, 16]

尾递归与蹦床函数

尾递归转换

// 普通递归 → 尾递归
// 关键:将计算结果作为参数传递

// 普通递归
function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1); // 不是尾递归
}

// 尾递归版本
function factorial(n, acc = 1) {
if (n <= 1) return acc;
return factorial(n - 1, n * acc); // 尾递归
}

// 数组求和
function sum(arr, acc = 0) {
if (arr.length === 0) return acc;
return sum(arr.slice(1), acc + arr[0]);
}

// 斐波那契
function fib(n, a = 0, b = 1) {
if (n === 0) return a;
if (n === 1) return b;
return fib(n - 1, b, a + b);
}

蹦床函数

// 解决不支持 TCO 的环境中的栈溢出问题
const trampoline = fn => (...args) => {
let result = fn(...args);
while (typeof result === 'function') {
result = result();
}
return result;
};

// 使用蹦床的安全递归
function safeFactorial(n, acc = 1) {
if (n <= 1) return acc;
return () => safeFactorial(n - 1, n * acc);
}

const factorial = trampoline(safeFactorial);
factorial(100000); // 安全执行

Lens 快速参考

基本操作

const R = require('ramda');

const data = {
user: {
profile: {
name: '张三',
address: { city: '北京' }
}
}
};

// 创建 Lens
const nameLens = R.lensPath(['user', 'profile', 'name']);
const cityLens = R.lensPath(['user', 'profile', 'address', 'city']);

// 读取: view(lens, data)
R.view(nameLens, data); // '张三'

// 设置: set(lens, value, data)
R.set(cityLens, '上海', data);

// 修改: over(lens, fn, data)
R.over(nameLens, R.toUpper, data);

// Lens 组合
const userLens = R.lensProp('user');
const profileLens = R.lensProp('profile');
const composedLens = R.compose(userLens, profileLens);

Ramda Lens 函数

函数用途示例
lensProp(prop)聚焦对象属性R.lensProp('name')
lensIndex(index)聚焦数组索引R.lensIndex(0)
lensPath(path)聚焦深层路径R.lensPath(['a', 'b', 'c'])
lens(getter, setter)自定义 LensR.lens(x => x.a, (v, x) => ({...x, a: v}))
view(lens, data)读取值R.view(nameLens, data)
set(lens, value, data)设置值R.set(nameLens, '李四', data)
over(lens, fn, data)修改值R.over(nameLens, R.toUpper, data)

常用工具函数

数组操作

// 分组
const groupBy = key => arr =>
arr.reduce((acc, item) => {
const k = item[key];
(acc[k] = acc[k] || []).push(item);
return acc;
}, {});

// 去重
const unique = arr => [...new Set(arr)];
const uniqueBy = key => arr =>
arr.filter((item, i) =>
arr.findIndex(x => x[key] === item[key]) === i
);

// 扁平化
const flatten = arr => arr.flat(Infinity);

// 分块
const chunk = size => arr =>
Array.from({ length: Math.ceil(arr.length / size) }, (_, i) =>
arr.slice(i * size, i * size + size)
);

// 交集、并集、差集
const intersection = (a, b) => a.filter(x => b.includes(x));
const union = (a, b) => [...new Set([...a, ...b])];
const difference = (a, b) => a.filter(x => !b.includes(x));

对象操作

// 深拷贝(简单版)
const deepClone = obj => JSON.parse(JSON.stringify(obj));

// 深合并
const deepMerge = (target, source) => {
const result = { ...target };
for (const key in source) {
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
result[key] = deepMerge(result[key] || {}, source[key]);
} else {
result[key] = source[key];
}
}
return result;
};

// 对象选择
const pick = keys => obj =>
keys.reduce((acc, key) => {
if (key in obj) acc[key] = obj[key];
return acc;
}, {});

// 对象排除
const omit = keys => obj => {
const result = { ...obj };
keys.forEach(key => delete result[key]);
return result;
};

函数工具

// 恒等函数
const identity = x => x;

// 常量函数
const always = x => () => x;

// 条件执行
const when = (pred, fn) => x => pred(x) ? fn(x) : x;
const unless = (pred, fn) => x => !pred(x) ? fn(x) : x;

// 默认值
const defaultTo = defaultValue => value =>
value == null ? defaultValue : value;

// 组合谓词
const and = (...preds) => x => preds.every(p => p(x));
const or = (...preds) => x => preds.some(p => p(x));
const not = pred => x => !pred(x);

// 交换参数顺序
const flip = fn => (a, b) => fn(b, a);

表单验证模式

// 验证器工厂
const required = msg => value =>
value == null || value === '' ? msg : null;

const minLength = min => msg => value =>
value && value.length < min ? msg : null;

const maxLength = max => msg => value =>
value && value.length > max ? msg : null;

const pattern = regex => msg => value =>
value && !regex.test(value) ? msg : null;

const email = msg => pattern(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)(msg);

// 组合验证器
const composeValidators = (...validators) => value => {
for (const validate of validators) {
const error = validate(value);
if (error) return error;
}
return null;
};

// 使用示例
const validatePassword = composeValidators(
required('密码不能为空'),
minLength(8)('密码至少8个字符'),
maxLength(100)('密码不能超过100个字符'),
pattern(/[A-Z]/)('密码必须包含大写字母'),
pattern(/[a-z]/)('密码必须包含小写字母'),
pattern(/[0-9]/)('密码必须包含数字')
);

最佳实践清单

函数设计

  • 每个函数只做一件事
  • 函数名清晰表达意图
  • 参数数量合理(不超过 3-4 个)
  • 最可能变化的参数放最后
  • 避免副作用,或隔离副作用

数据处理

  • 使用 mapfilterreduce 替代循环
  • 保持数据不可变
  • 使用展开运算符复制数组/对象
  • 避免直接修改参数

代码组织

  • 将重复逻辑提取为函数
  • 使用组合构建复杂功能
  • 合理使用柯里化
  • 避免过长的函数链

错误处理

  • 使用 Maybe 处理可能为空的值
  • 使用 Either 携带错误信息
  • 避免抛出异常,使用返回值表示错误

性能考虑

  • 使用 useMemo/useCallback 避免不必要计算
  • 大数据集考虑惰性求值
  • 避免在循环中频繁创建函数

参考资源