13.1 纯函数与副作用

13.1 纯函数与副作用

函数式编程(Functional Programming)是 JavaScript 中的重要范式,而纯函数(Pure Function)和副作用(Side Effect)是理解函数式编程的核心概念。本节将详细介绍纯函数的定义、特点以及副作用的影响,并通过示例代码展示如何编写纯函数和避免副作用。


13.1.1 纯函数(Pure Function)

1. 定义
纯函数是指满足以下两个条件的函数:

  • 相同的输入始终返回相同的输出(确定性)。
  • 不依赖或改变外部状态(无副作用)。

2. 示例

// 纯函数
function add(a, b) {
  return a + b;
}
console.log(add(2, 3)); // 总是输出 5

3. 特点

  • 可缓存性:由于输入输出恒定,可以缓存结果。
  • 可测试性:不依赖外部状态,易于单元测试。
  • 并行安全:无共享状态,适合并行执行。

13.1.2 副作用(Side Effect)

1. 定义
副作用是指函数在执行过程中对外部状态产生的影响,例如:

  • 修改全局变量。
  • 修改输入参数。
  • 发起网络请求。
  • 操作 DOM。

2. 示例

// 有副作用的函数
let count = 0;
function increment() {
  count++; // 修改外部变量
  return count;
}
console.log(increment()); // 输出 1(结果依赖外部状态)

13.1.3 纯函数 vs 非纯函数

特性 纯函数 非纯函数
输入输出确定性 ✅ 相同输入总是相同输出 ❌ 输出可能依赖外部状态
副作用 ❌ 无 ✅ 可能修改外部状态或依赖外部状态
可测试性 ✅ 高 ❌ 低(需模拟外部环境)
可缓存性 ✅ 可缓存 ❌ 不可缓存

13.1.4 如何避免副作用?

1. 不修改输入参数

// 非纯函数(修改输入参数)
function updateUser(user) {
  user.age = 25; // 直接修改参数
  return user;
}

// 纯函数(返回新对象)
function updateUserPure(user) {
  return { ...user, age: 25 }; // 使用展开运算符创建新对象
}

2. 隔离副作用
将副作用限制在可控范围内(如函数组合的最外层):

// 将副作用隔离到单独的函数中
function fetchData(url) {
  return fetch(url); // 副作用(网络请求)在此发生
}

// 纯函数处理数据
function processData(data) {
  return data.filter(item => item.isActive);
}

3. 使用不可变数据
通过库(如 Immutable.js)或原生方法(如 Object.freeze)避免直接修改数据:

const user = Object.freeze({ name: "Alice", age: 24 });
function updateAge(user, newAge) {
  return { ...user, age: newAge }; // 返回新对象
}

13.1.5 示例代码

示例 1:纯函数

// 纯函数:计算数组平均值
function calculateAverage(numbers) {
  const sum = numbers.reduce((acc, num) => acc + num, 0);
  return sum / numbers.length;
}

console.log(calculateAverage([1, 2, 3])); // 输出 2

示例 2:隔离副作用

// 非纯部分(副作用)
function logMessage(message) {
  console.log(message); // 副作用(I/O 操作)
}

// 纯函数部分
function formatMessage(user, text) {
  return `${user.name}: ${text}`;
}

const user = { name: "Alice" };
const message = formatMessage(user, "Hello");
logMessage(message); // 输出 "Alice: Hello"

示例 3:避免修改输入

// 非纯函数(修改输入)
function addToCartImpure(cart, item) {
  cart.push(item); // 直接修改输入参数
  return cart;
}

// 纯函数(返回新数组)
function addToCartPure(cart, item) {
  return [...cart, item]; // 使用展开运算符创建新数组
}

13.1.6 总结

  • 纯函数:输入输出确定、无副作用,适合核心逻辑。
  • 副作用:需谨慎控制,尽量隔离到代码外层。
  • 实践建议
    • 优先编写纯函数。
    • 将副作用(如 API 调用、DOM 操作)集中管理。
    • 使用不可变数据避免意外修改。

通过理解纯函数和副作用,你可以编写更可预测、更易维护的代码。在接下来的学习中,我们将探索函数式编程的其他高级特性。


思考题

  1. 为什么纯函数更适合单元测试?
  2. 如何将一个依赖全局变量的非纯函数改造成纯函数?
  3. 在哪些场景下副作用是不可避免的?如何管理它们?
#前端开发 分享于 2025-03-24

【 内容由 AI 共享,不代表本站观点,请谨慎参考 】