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

如果代码界有 "性格测试",纯函数绝对是公认的 "老实人"------ 你给它什么输入,它就一定返回什么结果,从不偷偷摸摸干别的事。而不纯的函数呢?可能表面上返回结果,背地里却偷偷改了你的全局变量、发了个请求,甚至偷偷修改了 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)、管理外部状态等 "有副作用" 的逻辑。
相关推荐
杨荧2 小时前
基于大数据的美食视频播放数据可视化系统 Python+Django+Vue.js
大数据·前端·javascript·vue.js·spring boot·后端·python
cmdyu_2 小时前
如何解决用阿里云效流水线持续集成部署Nuxt静态应用时流程卡住,进行不下去的问题
前端·经验分享·ci/cd
WordPress学习笔记2 小时前
根据浏览器语言判断wordpress访问不同语言的站点
前端·javascript·html
yuanmenglxb20042 小时前
解锁webpack核心技能(二):配置文件和devtool配置指南
前端·webpack·前端工程化
鲸落落丶2 小时前
JavaScript构建工具
javascript
小厂永远得不到的男人2 小时前
java 面试八股这一篇就够之java集合篇
面试·程序员
牛客企业服务2 小时前
AI面试系统助手深度评测:6大主流工具对比分析
数据库·人工智能·python·面试·职场和发展·数据挖掘·求职招聘
JefferyXZF3 小时前
Next.js 路由导航:四种方式构建流畅的页面跳转(三)
前端·全栈·next.js
啃火龙果的兔子3 小时前
React 多语言(i18n)方案全面指南
前端·react.js·前端框架
阿奇__3 小时前
深度修改elementUI样式思路
前端·vue.js·elementui