函数式编程里面有一个重要的概念,纯函数,即怎样的输入有怎样的输出,并且不依赖于外部变量,同时函数也不会产生任何可观察的副作用。那么什么是函数副作用呢?
函数副作用是指函数在正常工作任务之外对外部环境所施加的影响。
具体地说,函数副作用是指函数被调用,完成了函数既定的计算任务,
1,但同时因为访问了外部数据,尤其是因为对外部数据进行了写操作,从而一定程度地改变了系统环境。
2,函数的副作用也有可能是发生在函数运行期间,由于对外部数据的改变,导致了同步运行的外部函数受到影响。
总结:重要的一点是有没有改变系统环境。
比如一个纯函数在运行期间调用了另外一个纯函数,虽然调用了外部变量,但是该函数仍然是纯函数,因为该函数并没有改变系统环境。
什么是可观察的副作用:
在函数内部与其外部的任意交互。这可能是在函数内修改外部的变量
常见的副作用实例:
-
进行一个 HTTP 请求
-
Mutating data
-
输出数据到屏幕或者控制台
-
DOM 查询/操作
-
Math.random()
-
获取的当前时间
副作用的意义
副作用从名字上看似乎是不好的,但是实际应用中副作用有很大的意义。
在探讨副作用的意义之前,我们先来看下纯函数的意义。
1,函数式编程中大量使用纯函数
函数式编程可以接收函数作为入参,并且可以返回函数,从代码的可维护性上讲,函数式编程最大的好处是引用透明,即函数运行的结果只依赖于输入的参数,而不依赖于外部状态
函数式编程的精髓:业务系统模型无状态。模型的好坏,直接影响到代码的正确性、可靠性、稳定性,以及是否需要996。
2,方便代码重构
由于纯函数只和入参有关,这时候重构该函数时对系统几乎没有影响
3,方便测试,可预测性增强,降低代码管理的难度
不需要考虑上下文,只考虑输入输出
4,健壮性
改变纯函数的执行次序,并不会对结果造成影响
实例:redux的reducer出=纯函数
reducer每次返回一个新的状态,而不是去改变旧的状态,避免了对象的深比较,而深比较是比较昂贵的。
与之相对的是函数副作用
函数副作用的良性意义:
1,通过指针和引用参数,主动让被调函数获得外部数据访问权,以便提高数据处理的性能,比如实现缓存机制
2,改变全局变量,比如计数,根据全局变量的不同的值,调用不同的接口
3,通过返回指针或引用的做法,获得共享堆空间,以简化计算工作。
缓存设计实例:
比如,有函数getRseource(path)根据获得资源,如果没有副作用,每次都要执行存储,有了副作用之后(外部变量),就可以简化操作
var UtilFiles;
getFileList: function(path) {
if (UtilFiles) return UtilFiles;
if (!fs.existsSync(path)) {
throw new Error("can't find pages directory.");
}
var files = fs.readdirSync(path);
if (!files || files.length === 0) {
throw new Error("can't find any page");
}
files = files.filter(function(f) {
return fs.existsSync(pageDir + "/" + f + "/app.js");
});
if (files.length === 0) {
throw new Error("can't find any page");
}
UtilFiles = files;
return files;
},
说明:
- 通过var创建的全局变量(任何函数之外的程序中创建)是不能被删除的。
- 无var创建的隐式全局变量(无视是否在函数中创建)是能被删除的。
隐式全局变量并不是真正的全局变量,但它们是全局对象的属性
参考:https://baike.baidu.com/item/%E5%87%BD%E6%95%B0%E5%89%AF%E4%BD%9C%E7%94%A8/22723425?fr=aladdin
https://www.liaoxuefeng.com/article/1260118907809920