纯函数:相同输入必出相同输出?这才是代码界的 “老实人”

如果代码界有 "性格测试",纯函数绝对是公认的 "老实人"------ 你给它什么输入,它就一定返回什么结果,从不偷偷摸摸干别的事。而不纯的函数呢?可能表面上返回结果,背地里却偷偷改了你的全局变量、发了个请求,甚至偷偷修改了 DOM。今天我们就来聊聊纯函数这个 "老实人" 的特性、优势,以及在实际开发中如何用好它。

什么是纯函数?用两个例子说清楚

纯函数的定义很简单,满足两个条件即可:

  1. 相同的输入,必然得到相同的输出
  2. 没有副作用(不修改函数外部的状态,不影响函数外的世界)。

(1)纯函数:像数学公式一样靠谱

javascript 复制代码
// 纯函数:add(a, b)
function add(a, b) {
  return a + b;
}

// 无论调用多少次,只要输入相同,结果就一定相同
console.log(add(2, 3)); // 永远是5
console.log(add(2, 3)); // 还是5

这个函数就像数学里的 "加法公式"------ 输入23,输出必然是5,而且它不会对函数外部的任何变量、状态产生影响。

(2)非纯函数:偷偷摸摸干 "坏事"

javascript 复制代码
// 非纯函数:addToTotal(a)
let total = 0; // 外部变量
function addToTotal(a) {
  total += a; // 修改了外部变量(副作用)
  return total;
}

// 相同的输入,输出可能不同!
console.log(addToTotal(2)); // 输出2(此时total=2)
console.log(addToTotal(2)); // 输出4(此时total=4)

这个函数就 "不老实" 了:

  • 它修改了外部的total变量(这就是 "副作用");
  • 同样输入2,第一次返回2,第二次返回4,违反了 "相同输入必同输出" 的规则。

"副作用" 到底是什么?纯函数的红线

纯函数的核心禁忌是 "副作用",它指的是函数对外部环境产生的任何影响,常见的副作用包括:

  • 修改全局变量、外部对象的属性;
  • 发送网络请求(fetch/axios);
  • 操作 DOM(如document.getElementById修改内容);
  • 调用Math.random()Date.now()等 "不确定性" 函数(输入相同,输出可能不同)。

举个例子,下面这些都是非纯函数:

javascript 复制代码
// 1. 修改外部对象
const obj = { name: '张三' };
function changeName(newName) {
  obj.name = newName; // 副作用:修改了外部对象
  return obj.name;
}

// 2. 发送网络请求
function fetchUser(id) {
  return fetch(`/api/user/${id}`); // 副作用:发请求
}

// 3. 依赖随机数
function getRandom() {
  return Math.random(); // 相同输入(无输入),输出不同
}

为什么要在意纯函数?它的 3 个核心优势

纯函数看似 "限制多",但在实际开发中却能带来巨大好处,尤其是在大型项目中。

(1)可预测性:代码行为完全可控

纯函数的 "相同输入必同输出" 特性,让代码的行为变得可预测。比如调用add(2, 3),你不用看函数内部实现,就知道返回5;而调用非纯函数addToTotal(2),你必须跟踪total变量的变化才能知道结果。

这种可预测性让调试变得简单 ------ 如果结果不符合预期,只需检查输入和函数内部逻辑,不用怀疑外部因素。

(2)可缓存性:重复计算可以 "偷懒"

由于纯函数的输出只依赖输入,对于相同的输入,可以缓存结果,避免重复计算。这在处理复杂计算时能大幅提升性能。

javascript 复制代码
// 纯函数:计算平方
function square(n) {
  console.log('计算平方...');
  return n * n;
}

// 缓存函数:缓存纯函数的结果
function memoize(fn) {
  const cache = {};
  return function (arg) {
    if (cache[arg] !== undefined) {
      console.log('从缓存获取');
      return cache[arg];
    }
    const result = fn(arg);
    cache[arg] = result;
    return result;
  };
}

// 包装纯函数
const memoizedSquare = memoize(square);

memoizedSquare(3); // 输出"计算平方...",返回9
memoizedSquare(3); // 输出"从缓存获取",返回9(无需重复计算)

(3)可测试性:单元测试变得简单

测试纯函数时,不需要搭建复杂的环境(如模拟 DOM、网络请求),只需直接断言 "输入→输出" 是否符合预期。

javascript 复制代码
// 测试纯函数add
test('add(2, 3) should return 5', () => {
  expect(add(2, 3)).toBe(5); // 简单直接,无需准备环境
});

// 测试非纯函数addToTotal(麻烦)
test('addToTotal(2) should return 2 first', () => {
  total = 0; // 必须先重置外部变量
  expect(addToTotal(2)).toBe(2);
});

误区:纯函数不是 "银弹"

纯函数虽好,但不能解决所有问题。实际开发中,我们需要 "纯函数" 和 "非纯函数" 配合:

  • 纯函数:负责处理数据计算、状态转换等 "无副作用" 的逻辑;
  • 非纯函数:负责处理 IO 操作(发请求、操作 DOM)、管理外部状态等 "有副作用" 的逻辑。
相关推荐
gnip18 分钟前
实现AI对话光标跟随效果
前端·javascript
脑花儿1 小时前
ABAP SMW0下载Excel模板并填充&&剪切板方式粘贴
java·前端·数据库
闭着眼睛学算法2 小时前
【华为OD机考正在更新】2025年双机位A卷真题【完全原创题解 | 详细考点分类 | 不断更新题目 | 六种主流语言Py+Java+Cpp+C+Js+Go】
java·c语言·javascript·c++·python·算法·华为od
烛阴2 小时前
【TS 设计模式完全指南】构建你的专属“通知中心”:深入观察者模式
javascript·设计模式·typescript
ShineSpark2 小时前
C++面试11——指针与引用
c++·面试
lumi.2 小时前
Vue.js 从入门到实践1:环境搭建、数据绑定与条件渲染
前端·javascript·vue.js
二十雨辰2 小时前
vue核心原理实现
前端·javascript·vue.js
影子信息2 小时前
[Vue warn]: Error in mounted hook: “ReferenceError: Jessibuca is not defined“
前端·javascript·vue.js
卷Java3 小时前
CSS模板语法修复总结
java·前端·css·数据库·微信小程序·uni-app·springboot
北城以北88883 小时前
JavaScript--基础ES(一)
开发语言·javascript·intellij-idea