理解副作用:函数设计中的不可预测性

149 阅读2分钟

什么是副作用?

我们通常把“函数”定义为:接受输入(参数),经过计算后返回输出(返回值)的过程。如果一个函数除了这个过程之外,还依赖于输入参数以外的状态,或者对程序本身造成了可观察到的变化,那么我们就说这个函数有“副作用”。

例子解析

  1. 有副作用的函数:

    function addTodo(todo) {
      todos.push(todo);
    }
    

    这个函数依赖于外部的 todos 变量(不是通过参数传入的),并且通过 push 方法修改了 todos 的内容。这两个行为都属于副作用。

  2. 依赖外部状态的函数:

    function getGithubProfile(username) {
      return fetch(
        `https://api.github.com/users/${username}`
      ).then((res) => res.json());
    }
    

    这个函数没有直接修改程序的状态,但它依赖于外部的 API(即外部状态),这也算作副作用。

  3. 修改外部状态的函数:

    function updateDocumentTitle(title) {
      document.title = title;
    }
    

    这个函数没有依赖外部状态(只用到了参数),但它修改了 document.title,即对外部环境产生了可观察的变化,因此也有副作用。

为什么副作用“有问题”?

副作用并不是“坏”的,但它们会让代码变得不可预测。因为副作用依赖于函数外部的上下文(比如全局变量、外部 API、浏览器环境等),如果这些外部状态发生变化,函数的行为也会随之变化,导致结果不可控。

比如:

function calculateFinalPrice(price, qty) {
  const total = price * qty
  return total * (1 + TAX_RATE)
}

这个函数依赖于外部变量 TAX_RATE。如果 TAX_RATE 被修改甚至删除,calculateFinalPrice 的行为就会变得不可预测,这就是副作用带来的问题。

总结

副作用指的是函数除了根据输入返回输出之外,还依赖或改变了外部状态。副作用会让代码变得难以预测和维护,但在实际开发中,副作用是不可避免的(比如网络请求、修改页面内容等)。关键在于如何管理和控制副作用,使代码更易于理解和维护。