副作用

副作用(Side Effect) 在编程中指的是函数或操作除了返回值之外,还对程序的其他部分产生影响,或者依赖外部状态。简单来说,副作用是指那些影响函数外部的状态、环境或与外部交互的操作。

常见的副作用举例:

  1. 网络请求:请求数据并更新应用状态。

  2. 文件 I/O:读写文件、存储数据等操作。

  3. 修改全局变量或外部状态:在函数内部改变全局变量,或改变传入的引用类型参数。

  4. 访问数据库:读取或写入数据库的数据。

  5. 定时器:如 setTimeoutsetInterval,它们在未来某个时刻会执行一些操作。

  6. 控制台输出:像 printconsole.log 这样的函数调用,它们输出信息到控制台。

  7. DOM 操作:直接操作页面元素,改变网页内容。

副作用与纯函数的关系

在函数式编程中,纯函数 是指不产生副作用的函数,即:

  • 输入确定,输出也确定:同样的输入总是返回同样的结果。

  • 无副作用:函数只依赖于输入参数,且不会影响外部状态。

例如:

func add(a: Int, b: Int) -> Int {
    return a + b
}

这就是一个纯函数,因为它没有副作用。

但如果你有如下代码:

var globalVariable = 0

func incrementGlobalVariable() {
    globalVariable += 1
}

这里的 incrementGlobalVariable 就有副作用,它修改了 globalVariable 这个外部变量的状态。

副作用的重要性

副作用在编程中是不可避免的,尤其在开发用户界面、网络交互、数据存储等操作时。但为了提高代码的可维护性、可预测性,程序设计通常倾向于将副作用隔离到特定区域或函数中。例如,在现代前端框架(如 React、SwiftUI、TCA 等)和函数式编程语言中,副作用往往通过特殊的机制管理或封装起来。

副作用的管理

在框架中,比如 ReactSwiftUI,副作用通常由框架帮助管理,以避免不必要的重复渲染或不期望的状态变更。例如,React 使用 useEffect 来处理副作用,而 TCA(The Composable Architecture) 使用 Effect 类型专门处理异步操作和外部系统的交互,比如网络请求、数据库操作等。

在 React 中:

useEffect(() => {
  // 这里的代码是一个副作用,比如网络请求、订阅等
  console.log("副作用产生");

  return () => {
    // 清理副作用,如取消订阅
    console.log("清理副作用");
  };
}, []); // 依赖数组为空,只在组件挂载和卸载时运行

在 TCA 中:

Effect.run { subscriber in
    // 这里是副作用逻辑,比如网络请求、异步任务
    subscriber.sendCompletion(.finished)
    return AnyCancellable {}
}

总结:

副作用是那些在函数执行过程中产生的、影响函数外部的操作。它们虽然不可避免,但应当通过良好的管理方式来确保代码的可预测性、可维护性。这就是为什么框架和编程语言会引入机制来隔离和处理副作用。

Last updated