什么是函数副作用?
一、函数副作用是指当调用函数时,除了返回函数值之外,还对主调用函数产生附加的影响。副作用的函数不仅仅只是返回了一个值,而且还做了其他的事情,比如:
- 修改了一个变量
- 修改dom
- ajax
- 计时器
- 存储相关
- 直接修改数据结构
- 设置一个对象的成员
- 抛出一个异常或以一个错误终止
- 打印到终端或读取用户输入
- console.log()
- 读取或写入一个文件
- 在屏幕上画图
- 配置文件
- 数据库
- 获取数据的输入
- …
二、副作用主要体现在是否有全局污染,可维护性如何,扩展性是否良好等。
三、函数副作用会给程序设计带来不必要的麻烦,给程序带来十分难以查找的错误,并且降低程序的可读性,严格的函数式语言要求函数必须无副作用。
无副作用(side-effect-free)
webpack的tree shaking提到副作用:「副作用」的定义是,在导入时会执行特殊行为的代码,而不是仅仅暴露一个 export 或多个 export。举例说明,例如 polyfill,它影响全局作用域,并且通常不提供 export。
纯函数 ( Pure Function )
一、输入输出数据流全是显式(Explicit)的。
- 显式(Explicit)的意思是,函数与外界交换数据只有一个唯一渠道——参数和返回值。
- 函数从函数外部接受的所有输入信息都通过参数传递到该函数内部。
- 函数输出到函数外部的所有信息都通过返回值传递到该函数外部。
二、数组的slice、splice分别是纯函数、非纯函数
- slice返回数组中的指定部分,不会改变原数组。
- splice对数组进行操作返回该数组,会改变原数组。
三、函数式编程不会保留计算中间的结果,所以变量是不可变的(无状态的)。
好处
一、纯函数的好处
- 可缓存:因为纯函数对相同输入始终有相同的结果,所以可以把纯函数的结果缓存起来
- 可测试:纯函数让测试更方便
- 并行处理
- 在多线程环境下并行操作分享的内存数据很可能出现意外情况
- 纯函数不需要访问共享数据内存,所以在并行环境下可以任意运行除函数(web worker)
非纯函数 ( Impure Function )
一、与之相反。 隐式(Implicit)的意思是,函数通过参数和返回值以外的渠道,和外界进行数据交换。比如读取/修改全局变量,都叫作以隐式的方式和外界进行数据交换。引用透明 ( Referential Transparent )
一、引用透明的概念与函数的副作用相关,且受其影响。 如果程序中两个相同值得表达式能在该程序的任何地方互相替换,而不影响程序的动作,那么该程序就具有引用透明性。
二、它的优点是比非引用透明的语言的语义更容易理解,不那么晦涩。
三、纯函数式语言没有变量,所以它们都具有引用透明性。
| 【示例】以下示例说明了引用透明与函数副作用的结合,代码如下:```javascript result1 = (fun(a) + b) / (fun(a) -c); temp = fun(a); result2 = (temp + b) / (temp -c);
如果函数没有副作用,那么result1和result2将是等价的。然而如果fun有副作用,比如让b或c加1,那么result1和result2将不相等。因此,副作用违背了引用透明性。 |
| --- |
四、在JavaScript中,引入了函数。但显然js中的函数可以访问、修改全局变量(或定义在函数外的变量),如下
```javascript
var a = 5;
function fun(){
a = 10;
}
fun(); // a 变成了10
消除副作用
一、js中要想保证函数无副作用这项特性,只能依靠编程人员的习惯,即
- 函数入口使用参数运算,而不修改它
- 函数内不修改函数外的变量,如全局变量
- 运算结果通过函数返回给外部(出口)