Skip to content

函数式编程实践

数据转换管道

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: 如何在项目中使用函数式编程?

答案:

  1. 使用纯函数处理业务逻辑
  2. 使用不可变数据更新状态
  3. 利用函数组合简化复杂逻辑
  4. 使用函子处理错误和空值
  5. 封装副作用到边界

Q2: 函数式编程的优缺点?

优点:

  • 代码更简洁、可读
  • 易于测试和调试
  • 减少状态管理的复杂性
  • 更好的并行支持

缺点:

  • 学习曲线陡峭
  • 性能开销(不可变数据)
  • 调试堆栈不直观
  • 与命令式代码交互复杂

Q3: 什么时候不适合用函数式编程?

答案:

  • 性能关键的代码(对象创建开销)
  • 简单的命令式任务
  • 团队不熟悉函数式概念
  • 需要直接操作 DOM 或 I/O

前端面试知识库