写点什么

前端开发中使用纯函数提纯非纯函数

用户头像
devpoint
关注
发布于: 2 小时前
前端开发中使用纯函数提纯非纯函数

理解纯函数和非纯函数是向更清晰、更基于角色和可测试的代码的简单过渡。在这篇文章中,将通过查看一个简单的体重指数(BMI)计算器来探索纯函数和非纯函数,该计算器通过一些简单的身高和体重输入因素来估算“健康体重”。

概念

上文简单介绍了什么是纯函数和非纯函数,这里在简单列举一下:


  • 纯函数:纯函数更容易理解,特别是因为代码库可以扩展,以及基于角色的函数可以完成一项工作并且做得很好。纯函数不会修改作用域之外的外部变量、状态、数据,并且在给定相同输入的情况下返回相同的输出,这样的函数就被认为是“纯”的。

  • 非纯函数:在其作用域范围之外改变变量、状态、数据的函数,因此因此将其视为“不纯”。编写 JavaScript 的方法有很多种,从非纯/纯函数的角度考虑,可以编写更容易推理的代码。


下面以完全不纯的方式创建的 BMI 计算器,然后再将其重构为多个使用纯函数的函数。

HTML 和事件

这是创建的用于捕获用户输入数据的表单:


<form name="bmi">    <h1>BMI计算器</h1>    <label>        <input type="text" name="weight" placeholder="体重 (kg)" />    </label>    <label>        <input type="text" name="height" placeholder="身高 (cm)" />    </label>    <button type="submit">立即计算</button>    <div class="calculation">        <div>BMI值: <span class="result"></span></div>        <div>健康指数:<span class="health"></span></div>    </div></form>
复制代码


下面为按钮增加事件监听器:


<script>    (() => {        const elForm = document.querySelector("form[name=bmi]");        const onSubmit = (event) => {            event.preventDefault();        };
elForm.addEventListener("submit", onSubmit, false); })();</script>
复制代码

非纯的实现

现在将删除 IIFE 和事件处理程序的内容,并专注于 onSubmit 函数:


const onSubmit = (event) => {    event.preventDefault();
let healthMessage; const result = elForm.querySelector(".result"); const health = elForm.querySelector(".health"); const weight = parseInt( elForm.querySelector("input[name=weight]").value, 10 ); const height = parseInt( elForm.querySelector("input[name=height]").value, 10 ); const bmi = (weight / (((height / 100) * height) / 100)).toFixed(1); if (bmi < 18.5) { healthMessage = "偏瘦体重"; } else if (bmi > 18.5 && bmi < 25) { healthMessage = "健康体重"; } else if (bmi > 25) { healthMessage = "肥胖体重"; }
result.innerHTML = bmi; health.innerHTML = healthMessage;};
复制代码


这就是 onSubmit 事件函数包含的所有内容,输入身高/体重,它就会用这些结果更新 DOM。现在,这就是非纯函数极难调试和理解的地方,特别是对于非前端开发人员来。当然为了代码的易读性,可以将其加些注释,如下:


const onSubmit = (event) => {    // 阻止表单实际提交    event.preventDefault();
let healthMessage; // 获取DOM result 和 health 的 <span> 标签以将结果注入 const result = elForm.querySelector(".result"); const health = elForm.querySelector(".health");
// 根据重量和身高值解析为基数为 10 的整数 const weight = parseInt( elForm.querySelector("input[name=weight]").value, 10 ); const height = parseInt( elForm.querySelector("input[name=height]").value, 10 ); const bmi = (weight / (((height / 100) * height) / 100)).toFixed(1); if (bmi < 18.5) { healthMessage = "偏瘦体重"; } else if (bmi > 18.5 && bmi < 25) { healthMessage = "健康体重"; } else if (bmi > 25) { healthMessage = "肥胖体重"; } // 将结果插入到相应的 DOM result.innerHTML = bmi; health.innerHTML = healthMessage;};
复制代码


看上去好像容易理解,然后需要大量的注释,很多时候都无法如何去描述。下面就以纯函数的方式对其重构。

纯函数的实现

在开始使用纯函数之前,在上面不纯的实现中,onSubmit 函数中做了太多的事情:


  • 从 DOM 中读取值

  • 将值解析为数字

  • 从解析值计算 BMI

  • 有条件地检查 BMI 结果并将正确的消息分配给未定义的变量 healthMessage

  • 将值写入 DOM


为了提纯实现,将要实现处理这些操作的函数:


  • 将值解析为数字并计算 BMI

  • 将绑定到 DOM 的正确消息返回

开始提纯

先从输入值解析计算 BMI 开始,专门针对这一段代码:


const weight = parseInt(elForm.querySelector("input[name=weight]").value, 10);const height = parseInt(elForm.querySelector("input[name=height]").value, 10);
const bmi = (weight / (((height / 100) * height) / 100)).toFixed(1);
复制代码


这涉及 parseInt() 计算 BMI 的公式,当在应用程序中的某个时刻重构或添加更多功能时,这不是很灵活并且很可能很容易出错(客户端输入是不可靠的)。


为了重构,只需要单独获取每个输入的 value 属性,并将它们委托给一个 getBMI 函数:


const weight = elForm.querySelector("input[name=weight]").value;const height = elForm.querySelector("input[name=height]").value;
const bmi = getBMI(weight, height);
复制代码


这个 getBMI 函数将是 100% 纯的,因为它接受参数并根据这些参数返回一个新的数据,给定相同的输入,将获得相同的输出。具体代码如下:


const getBMI = (weight, height) => {    const newWeight = parseInt(weight, 10);    const newHeight = parseInt(height, 10);    return (newWeight / (((newHeight / 100) * newHeight) / 100)).toFixed(1);};
复制代码


此函数将 weightheight 作为参数,将它们转换为数字 parseInt ,然后执行 BMI 的计算。无论是将 String 还是 Number 作为每个参数传递,都可以安全检查。


进入下一个函数。healthMessage 的计算逻辑,如下:


health.innerHTML = getHealthMessage(bmi);
复制代码


同样,开始实现 getHealthMessage 的逻辑:


const getHealthMessage = (bmiValue) => {    let healthMessage;    if (bmiValue < 18.5) {        healthMessage = "偏瘦体重";    } else if (bmiValue > 18.5 && bmiValue < 25) {        healthMessage = "健康体重";    } else if (bmiValue > 25) {        healthMessage = "肥胖体重";    }    return healthMessage;};
复制代码


上面的函数也是 100% 的纯函数,输入输出固定。


至此,代码就完成了重构,完整代码如下:


<script>    (() => {        const elForm = document.querySelector("form[name=bmi]");
const getHealthMessage = (bmiValue) => { let healthMessage; if (bmiValue < 18.5) { healthMessage = "偏瘦体重"; } else if (bmiValue > 18.5 && bmiValue < 25) { healthMessage = "健康体重"; } else if (bmiValue > 25) { healthMessage = "肥胖体重"; } return healthMessage; };
const getBMI = (weight, height) => { let newWeight = parseInt(weight, 10); let newHeight = parseInt(height, 10); return ( newWeight / (((newHeight / 100) * newHeight) / 100) ).toFixed(1); };
const onSubmit = (event) => { event.preventDefault();
const result = elForm.querySelector(".result"); const health = elForm.querySelector(".health");
const weight = elForm.querySelector("input[name=weight]").value; const height = elForm.querySelector("input[name=height]").value;
const bmi = getBMI(weight, height);
result.innerHTML = bmi; health.innerHTML = getHealthMessage(bmi); };
elForm.addEventListener("submit", onSubmit, false); })();</script>
复制代码


在前端项目开发中很难保证所有函数都是纯函数,因为只要需要和 DOM 进行交互的函数就不是纯函数,因此在代码优化过程中,不要过份追求纯函数,尽可能的提纯函数即可。


发布于: 2 小时前阅读数: 4
用户头像

devpoint

关注

细节的追求者 2011.11.12 加入

专注前端开发,用技术创造价值!

评论

发布
暂无评论
前端开发中使用纯函数提纯非纯函数