Skip to content

函数式编程核心概念

纯函数(Pure Function)

定义: 相同输入始终返回相同输出,且不产生副作用。

javascript
// ❌ 非纯函数(依赖外部状态)
let counter = 0
function increment() {
  counter++
  return counter
}

// ✅ 纯函数(无外部依赖,无副作用)
function add(a, b) {
  return a + b
}

// ❌ 非纯函数(有副作用)
function saveUser(user) {
  user.updatedAt = new Date()  // 修改传入对象
  db.save(user)                // 副作用:数据库操作
}

// ✅ 纯函数
function createUpdatedUser(user) {
  return {
    ...user,
    updatedAt: new Date()
  }
}

纯函数的优势:

  • 可缓存(memoize)
  • 可测试
  • 可并行执行
  • 结果可预测

透明引用(Referential Transparency)

定义: 表达式可以用其值替换而不改变程序行为。

javascript
// 透明引用示例
const add = (a, b) => a + b
const result = add(1, 2)  // 可以替换为 3

// 非透明引用
let x = 10
const addToX = y => x + y
// addToX(5) 不能替换为 15,因为 x 可能变化

副作用(Side Effects)

定义: 除了返回值之外,对外部环境产生的任何影响。

javascript
// 常见副作用
function withSideEffects() {
  // 1. 修改外部变量
  globalVar = 'changed'

  // 2. 修改传入参数
  obj.x = 100

  // 3. I/O 操作
  console.log('log')  // 输出
  fetch(url)          // 网络请求
  readFile(path)      // 读文件

  // 4. 抛出异常
  throw new Error('error')

  // 5. 定时器
  setTimeout(() => {}, 1000)

  return 'result'
}

处理副作用的策略:

javascript
// 将副作用延迟到边界处理
const pureAddUser = (user) => ({
  type: 'ADD_USER',
  payload: user
})

const addUser = (user) => {
  // 副作用在这里(可测试的边界)
  return db.save(user)
    .then(saved => ({ type: 'ADD_USER_SUCCESS', payload: saved }))
    .catch(error => ({ type: 'ADD_USER_ERROR', payload: error }))
}

柯里化(Currying)

定义: 将多参数函数转换为一系列单参数函数。

javascript
// 普通函数
const add = (a, b, c) => a + b + c
add(1, 2, 3)  // 6

// 柯里化版本
const curryAdd = a => b => c => a + b + c
curryAdd(1)(2)(3)  // 6

// curry 工具函数
const curry = fn => (...args) => {
  if (args.length >= fn.length) return fn(...args)
  return (...more) => fn(...args, ...more)
}

const add = (a, b, c) => a + b + c
const curryAdd = curry(add)
curryAdd(1)(2)(3)  // 6
curryAdd(1, 2)(3)  // 6
curryAdd(1)(2, 3)  // 6
curryAdd(1, 2, 3)  // 6

// 实际应用
const fetchWithAuth = curry((token, url, options) => {
  return fetch(url, { ...options, headers: { ...options.headers, token } })
})

const fetchWithToken = fetchWithAuth('my-token')
fetchWithToken('/api/users', { method: 'GET' })
fetchWithToken('/api/posts', { method: 'POST' })

函数组合(Function Composition)

定义: 将多个函数串联起来,一个函数的输出作为下一个函数的输入。

javascript
// 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 toUpper = str => str.toUpperCase()
const split = sep => str => str.split(sep)
const join = sep => arr => arr.join(sep)
const first = arr => arr[0]

// compose 执行顺序:first → join → split → toUpper
const getFirstUpper = compose(
  first,
  join(', '),
  split(' '),
  toUpper
)

getFirstUpper('hello world from fp')  // 'HELLO'

// pipe 执行顺序更直观
const getFirstUpperPipe = pipe(
  str => str.toUpperCase(),
  str => str.split(' '),
  arr => arr[0]
)

getFirstUpperPipe('hello world from fp')  // 'HELLO'

高阶函数(Higher-Order Functions)

定义: 接收函数作为参数,或返回函数的函数。

数组方法

javascript
const users = [
  { name: 'Alice', age: 25, active: true },
  { name: 'Bob', age: 30, active: false },
  { name: 'Charlie', age: 35, active: true }
]

// map - 转换
const names = users.map(u => u.name)  // ['Alice', 'Bob', 'Charlie']

// filter - 过滤
const activeUsers = users.filter(u => u.active)

// reduce - 聚合
const totalAge = users.reduce((sum, u) => sum + u.age, 0)  // 90

// find - 查找
const bob = users.find(u => u.name === 'Bob')

// some - 是否存在
const hasUnder20 = users.some(u => u.age < 20)

// every - 是否全部
const allActive = users.every(u => u.active)

自定义高阶函数

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

const init = once(() => console.log('initialized'))
init()  // 'initialized'
init()  // 无输出

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

const fib = memoize(n => {
  if (n <= 1) return n
  return fib(n - 1) + fib(n - 2)
})

// partial - 部分应用
const partial = (fn, ...args) => (...moreArgs) => fn(...args, ...moreArgs)

const add = (a, b, c) => a + b + c
const add5 = partial(add, 5)
add5(3, 2)  // 10

不可变性(Immutability)

定义: 不修改原始数据,创建新的数据副本。

javascript
// ❌ 可变操作
const arr = [1, 2, 3]
arr.push(4)        // 修改原数组
arr[0] = 'changed' // 修改原数组

// ✅ 不可变操作
const newArr = [...arr, 4]      // [1, 2, 3, 4]
const updatedArr = arr.map(x => x === 2 ? 'changed' : x)

// 对象
const obj = { a: 1, b: 2 }
const newObj = { ...obj, b: 3 }  // { a: 1, b: 3 }
const updated = { ...obj, c: 3 } // { a: 1, b: 2, c: 3 }

// 深层不可变
const nested = { a: { b: { c: 1 } } }
const updatedNested = {
  ...nested,
  a: {
    ...nested.a,
    b: {
      ...nested.a.b,
      c: 2
    }
  }
}

不可变库:

javascript
// Immer - 更简洁的不可变操作
import { produce } from 'immer'

const state = { count: 0, todos: ['learn fp'] }
const newState = produce(state, draft => {
  draft.count++
  draft.todos.push('use immer')
})

面试高频题

Q1: 什么是纯函数?有什么好处?

答案: 纯函数是相同输入始终返回相同输出,且不产生副作用的函数。

好处:

  • 可缓存(性能优化)
  • 可测试
  • 可并行执行
  • 结果可预测,易于推理

Q2: 柯里化的作用是什么?

答案:

  • 将多参数函数转为单参数函数链
  • 提高函数的复用性和组合性
  • 延迟执行,灵活传参

Q3: 函数组合相比嵌套函数调用的优势?

答案:

  • 更清晰的可读性
  • 更易于调试和测试
  • 符合管道式编程思想
javascript
// ❌ 嵌套
toUpper(first(join(',')(split(' ')(str))))

// ✅ 组合
pipe(str => str.split(' '), join(', '), toUpper, first)

Q4: map、filter、reduce 的区别?

答案:

  • map:转换,每个元素映射为新值,长度不变
  • filter:过滤,保留符合条件的元素
  • reduce:聚合,将数组归约为单个值

前端面试知识库