什么是函数副作用?

一、函数副作用是指当调用函数时,除了返回函数值之外,还对主调用函数产生附加的影响。副作用的函数不仅仅只是返回了一个值,而且还做了其他的事情,比如:

  1. 修改了一个变量
  2. 修改dom
  3. ajax
  4. 计时器
  5. 存储相关
    1. 直接修改数据结构
    2. 设置一个对象的成员
  6. 抛出一个异常或以一个错误终止
  7. 打印到终端或读取用户输入
    1. console.log()
  8. 读取或写入一个文件
  9. 在屏幕上画图
  10. 配置文件
  11. 数据库
  12. 获取数据的输入

二、副作用主要体现在是否有全局污染,可维护性如何,扩展性是否良好等。
三、函数副作用会给程序设计带来不必要的麻烦,给程序带来十分难以查找的错误,并且降低程序的可读性,严格的函数式语言要求函数必须无副作用。

无副作用(side-effect-free)

webpack的tree shaking提到副作用:「副作用」的定义是,在导入时会执行特殊行为的代码,而不是仅仅暴露一个 export 或多个 export。举例说明,例如 polyfill,它影响全局作用域,并且通常不提供 export。

纯函数 ( Pure Function )

一、输入输出数据流全是显式(Explicit)的。

  1. 显式(Explicit)的意思是,函数与外界交换数据只有一个唯一渠道——参数和返回值。
  2. 函数从函数外部接受的所有输入信息都通过参数传递到该函数内部。
  3. 函数输出到函数外部的所有信息都通过返回值传递到该函数外部。

二、数组的slice、splice分别是纯函数、非纯函数

  1. slice返回数组中的指定部分,不会改变原数组。
  2. splice对数组进行操作返回该数组,会改变原数组。

三、函数式编程不会保留计算中间的结果,所以变量是不可变的(无状态的)。

好处

一、纯函数的好处

  • 可缓存:因为纯函数对相同输入始终有相同的结果,所以可以把纯函数的结果缓存起来
  • 可测试:纯函数让测试更方便
  • 并行处理
    • 在多线程环境下并行操作分享的内存数据很可能出现意外情况
    • 纯函数不需要访问共享数据内存,所以在并行环境下可以任意运行除函数(web worker)

      非纯函数 ( Impure Function )

      一、与之相反。 隐式(Implicit)的意思是,函数通过参数和返回值以外的渠道,和外界进行数据交换。比如读取/修改全局变量,都叫作以隐式的方式和外界进行数据交换。

      引用透明 ( Referential Transparent )

      一、引用透明的概念与函数的副作用相关,且受其影响。 如果程序中两个相同值得表达式能在该程序的任何地方互相替换,而不影响程序的动作,那么该程序就具有引用透明性。
      二、它的优点是比非引用透明的语言的语义更容易理解,不那么晦涩。
      三、纯函数式语言没有变量,所以它们都具有引用透明性。

| 【示例】以下示例说明了引用透明与函数副作用的结合,代码如下:```javascript result1 = (fun(a) + b) / (fun(a) -c); temp = fun(a); result2 = (temp + b) / (temp -c);

  1. 如果函数没有副作用,那么result1result2将是等价的。然而如果fun有副作用,比如让bc1,那么result1result2将不相等。因此,副作用违背了引用透明性。 |
  2. | --- |
  3. 四、在JavaScript中,引入了函数。但显然js中的函数可以访问、修改全局变量(或定义在函数外的变量),如下
  4. ```javascript
  5. var a = 5;
  6. function fun(){
  7. a = 10;
  8. }
  9. fun(); // a 变成了10

消除副作用

一、js中要想保证函数无副作用这项特性,只能依靠编程人员的习惯,即

  1. 函数入口使用参数运算,而不修改它
  2. 函数内不修改函数外的变量,如全局变量
  3. 运算结果通过函数返回给外部(出口)