函数式编程实践
数据转换管道
javascript
// 组合函数构建数据处理管道
const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x)
const processUser = pipe(
// 1. 过滤有效数据
users => users.filter(u => u.active),
// 2. 提取必要字段
users => users.map(u => ({
id: u.id,
name: u.name,
email: u.email
})),
// 3. 脱敏处理
users => users.map(u => ({
...u,
email: u.email.replace(/@.*/, '@***')
})),
// 4. 按名称排序
users => users.sort((a, b) => a.name.localeCompare(b.name)),
// 5. 限制数量
users => users.slice(0, 10)
)
processUser(allUsers)错误处理模式
javascript
// Result 类型 - 分离成功与失败
class Result {
constructor(value, error) {
this.value = value
this.error = error
}
static success(value) { return new Result(value, null) }
static failure(error) { return new Result(null, error) }
isSuccess() { return this.error === null }
isFailure() { return this.error !== null }
map(fn) {
return this.isSuccess()
? Result.success(fn(this.value))
: this
}
flatMap(fn) {
return this.isSuccess() ? fn(this.value) : this
}
getOrElse(defaultValue) {
return this.isSuccess() ? this.value : defaultValue
}
getOrThrow() {
if (this.isFailure()) throw this.error
return this.value
}
}
// 使用
const validateEmail = email => {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
return regex.test(email)
? Result.success(email)
: Result.failure(new Error('Invalid email'))
}
const saveUser = user => {
if (!user.name) return Result.failure(new Error('Name required'))
if (!validateEmail(user.email).isSuccess()) return Result.failure(new Error('Invalid email'))
return Result.success(db.save(user))
}
const createUser = user => {
return saveUser(user)
.map(u => ({ ...u, createdAt: new Date() }))
.map(u => sendWelcomeEmail(u))
}
// 链式调用
const result = createUser({ name: 'Alice', email: 'alice@example.com' })
if (result.isSuccess()) {
console.log('User created:', result.value)
} else {
console.error('Error:', result.error)
}业务逻辑组合
javascript
// 策略模式 - 函数组合实现
const hasPermission = user => user.role === 'admin'
const isOwner = (user, resource) => resource.ownerId === user.id
const isPublic = resource => resource.visibility === 'public'
const canEdit = user => resource =>
hasPermission(user) || isOwner(user, resource) || isPublic(resource)
// 折扣策略
const percentageDiscount = percent => price => price * (1 - percent / 100)
const fixedDiscount = amount => price => Math.max(0, price - amount)
const tieredDiscount = tiers => price => {
for (const [threshold, discount] of tiers) {
if (price >= threshold) return discount(price)
}
return price
}
// 组合折扣
const combineDiscounts = (...discounts) => price =>
discounts.reduce((price, discount) => discount(price), price)
const summerSale = percentageDiscount(20)
const vipDiscount = fixedDiscount(50)
const applyDiscount = combineDiscounts(summerSale, vipDiscount)
applyDiscount(500) // 500 * 0.8 - 50 = 350状态管理
javascript
// 不可变状态更新
const createStore = (reducer, initialState) => {
let state = initialState
const listeners = []
const getState = () => state
const dispatch = action => {
const newState = reducer(state, action)
if (newState !== state) {
state = newState
listeners.forEach(l => l(state))
}
}
const subscribe = listener => {
listeners.push(listener)
return () => listeners.filter(l => l !== listener)
}
return { getState, dispatch, subscribe }
}
// reducer 必须是纯函数
const counterReducer = (state, action) => {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 }
case 'DECREMENT':
return { ...state, count: state.count - 1 }
case 'SET':
return { ...state, count: action.payload }
default:
return state
}
}
const store = createStore(counterReducer, { count: 0 })
store.dispatch({ type: 'INCREMENT' })
store.dispatch({ type: 'INCREMENT' })
store.dispatch({ type: 'SET', payload: 10 })
console.log(store.getState()) // { count: 10 }异步数据流
javascript
// 函数式 Promise 链
const fetchUsers = () => fetch('/api/users').then(r => r.json())
const fetchPosts = () => fetch('/api/posts').then(r => r.json())
// 并行执行
const fetchAll = (...tasks) => Promise.all(tasks.map(t =>()))
fetchAll(fetchUsers, fetchPosts)
.then(([users, posts]) => ({ users, posts }))
.then(({ users, posts }) => processData(users, posts))
.catch(handleError)
// 顺序执行
const pipePromises = (...fns) => init =>
fns.reduce((promise, fn) => promise.then(fn), Promise.resolve(init))
const pipeline = pipePromises(
() => fetchUsers(),
users => filterActiveUsers(users),
users => sortByName(users),
users => mapToDto(users)
)
pipeline().then(handleResult)
// 超时处理
const withTimeout = (promise, ms) => {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms)
)
])
}
withTimeout(fetchUsers(), 5000)
.then(handleUsers)
.catch(handleError)领域特定语言(DSL)
javascript
// 构建器模式 - 函数式实现
const query = (table) => ({
select: (...columns) => ({ ...query(table), columns }),
where: (condition) => ({ ...query(table), condition }),
orderBy: (column, dir = 'asc') => ({ ...query(table), orderBy: { column, dir } }),
limit: (n) => ({ ...query(table), limit: n }),
build: () => {
const sql = `SELECT ${query(table).columns?.join(', ') || '*'} FROM ${table}`
const where = query(table).condition ? ` WHERE ${query(table).condition}` : ''
const order = query(table).orderBy ? ` ORDER BY ${query(table).orderBy.column} ${query(table).orderBy.dir}` : ''
const limit = query(table).limit ? ` LIMIT ${query(table).limit}` : ''
return sql + where + order + limit
}
})
// 使用
const sql = query('users')
.select('id', 'name', 'email')
.where('active = true')
.orderBy('createdAt', 'desc')
.limit(10)
.build()
// SELECT id, name, email FROM users WHERE active = true ORDER BY createdAt DESC LIMIT 10常用库
Lodash/fp
javascript
import fp from 'lodash/fp'
const processData = fp.pipe(
fp.filter({ active: true }),
fp.groupBy('category'),
fp.mapValues(fp.map(fp.pick(['id', 'name']))),
fp.toPairs,
fp.filter(([_, users]) => users.length > 2),
fp.fromPairs
)Ramda
javascript
import R from 'ramda'
const processUser = R.pipe(
R.prop('address'),
R.defaultTo({}),
R.prop('city'),
R.defaultTo('Unknown')
)
processUser({ address: { city: 'Beijing' } }) // 'Beijing'
processUser({ address: {} }) // 'Unknown'
processUser({}) // 'Unknown'fp-ts(TypeScript)
typescript
import { Option, some, none, map, chain } from 'fp-ts/Option'
const parseAge = (age: string): Option<number> => {
const parsed = parseInt(age, 10)
return isNaN(parsed) ? none : some(parsed)
}
const doubleAge = (age: Option<number>): Option<number> =>
map(x => x * 2)(age)
const addOne = (age: Option<number>): Option<number> =>
chain(x => x > 0 ? some(x + 1) : none)(age)面试高频题
Q1: 如何在项目中使用函数式编程?
答案:
- 使用纯函数处理业务逻辑
- 使用不可变数据更新状态
- 利用函数组合简化复杂逻辑
- 使用函子处理错误和空值
- 封装副作用到边界
Q2: 函数式编程的优缺点?
优点:
- 代码更简洁、可读
- 易于测试和调试
- 减少状态管理的复杂性
- 更好的并行支持
缺点:
- 学习曲线陡峭
- 性能开销(不可变数据)
- 调试堆栈不直观
- 与命令式代码交互复杂
Q3: 什么时候不适合用函数式编程?
答案:
- 性能关键的代码(对象创建开销)
- 简单的命令式任务
- 团队不熟悉函数式概念
- 需要直接操作 DOM 或 I/O