阅读 255

JavaScript 纯函数

纯函数

第一次听说"纯函数(Pure Function)"这个术语时, 我也是比较疑惑的. 啥是纯函数? 为什么要使用纯函数? 以及和普通的JavaScript函数有什么区别吗?

带着这些疑问, 一起来看下.

什么是纯函数?

定义

  • 如果函数的调用参数相同, 则永远返回相同的结果. 它不依赖于程序执行期间函数外部任何状态或数据的变化, 只依赖于传入的参数
  • 纯函数不会产生任何可观察的副作用, 例如: 网络请求, 输入/输出设备, 或数据突变(mutation)等

如果一个函数符合上述2个要求, 它就是纯函数.

什么是可观察的副作用?

一个可以被观察的副作用是在函数内部与该函数外部的任意交互. 如: 在函数内修改外部的变量, 或在函数内调用另外一个函数等等.

如果纯函数调用纯函数, 不会产生副作用, 依然是纯函数

可被观察的副作用, 包括但不限于:

  • 进行一个HTTP请求
  • Mutating Data
  • 输出数据到屏幕或控制台
  • DOM 查询或操作
  • Math.random()
  • 获取当前时间
  • ...

副作用本身并没有什么不好, 在某些场景往往是必需的. 但对于要保持纯函数, 它不能包含任何副作用. 当然, 并非所有函数都必须是纯函数.

纯函数 示例

function sqrt(a) {
  return a * a
}
复制代码

该函数符合我们所说的2条纯函数的定义, 不依赖于任何外部输入, 不改变任何外部数据, 没有副作用. 每次输入同样的参数, 永远会获取相同的结果, 且不随程序执行的时机而有变化.

非纯函数 示例

function getName(obj){    
  return obj.name;
}
function getAge(obj){  
  return obj.age;
}
function sayHi(person){  
  console.log('I am' + getName(person) + ',and I am' + getAge(person) + 'years old');
  }
  
var Tom = {  name: 'TOM',  age: 26};

sayHi(Tom);
复制代码

sayHi不是纯函数,它依赖于getNamegetAge两个函数,若不小心或因业务需要, 必需要改变其中某个函数的功能,这将使得sayHi这个函数出现错误。 当网页变得复杂,且由多人维护的时候,bug调试会变得非常复杂。

纯函数的优点

纯函数在函数式编程中被大量使用, 诸如React.jsRedux等库都需要使用纯函数.

纯函数可以用在平常的JavaScript开发中使用, 也可以混合纯函数和非纯函数一起使用, 不一定非要限定某个编程范例中必须使用纯函数.

并非所有的函数都需要纯函数. 例如: 操作DOM的事件处理函数就不适合使用纯函数. 不过, 这种事件处理函数, 可以调用其他纯函数来处理, 以此减少项目中不纯函数的数量.

使用纯函数的主要原因是可测试性和重构

可测试性

纯函数非常容易进行单元测试,因为不需要考虑上下文环境,只需要考虑输入和输出, 如果传入相同的参数, 它们将始终产生相同的结果.

重构

纯函数还会使得维护和重构代码变容易.

你可以放心的重构一个函数, 不必担心没注意到的副作用, 而导致整个应用调试复杂/困难.

::: warning 如果项目中, 充斥着副作用, 那么函数/模块之间的逻辑可能互相交织耦合, 在后期新增逻辑时, 可能由于依赖复杂而难以重构.

更常见的是, 开发为了应付需求, 而不断引入新的副作用到原本的逻辑上, 从而导致代码变得越来越糟糕. :::

正确地使用纯函数, 可以生产更高质量的代码, 并且也是一种更加干净的编码方式.

此外, 纯函数不是JavaScript的专利. 想要了解更多内容, 可以查阅PureFunction-wiki

可移植性 / 自文档化(Portable / Self-Documenting)

由于纯函数是自给自足的,它需要的东西都在输入参数中已经声明,所以它可以任意移植到任何地方。

相关链接

文章分类
代码人生
文章标签