第三章 纯函数的好处
最后更新于:2022-04-02 04:17:26
[TOC]
## 实例
纯函数
```
var xs = [1,2,3,4,5];
// 纯的
xs.slice(0,3);
//=> [1,2,3]
xs.slice(0,3);
//=> [1,2,3]
xs.slice(0,3);
//=> [1,2,3]
```
不纯函数
```
// 不纯的
xs.splice(0,3);
//=> [1,2,3]
xs.splice(0,3);
//=> [4,5]
xs.splice(0,3);
//=> []
```
## 有哪些副作用
* 更改文件系统
* 往数据库插入记录
* 发送一个 http 请求
* 可变数据
* 打印/log
* 获取用户输入
* DOM 查询
* 访问系统状态
概括来讲,只要是跟函数外部环境发生的交互就都是副作用——这一点可能会让你怀疑无副作用编程的可行性。函数式编程的哲学就是假定副作用是造成不正当行为的主要原因
这并不是说,要禁止使用一切副作用,而是说,要让它们在可控的范围内发生
.
## 纯函数好处
函数是不同数值之间的特殊关系:每一个输入值返回且只返回一个输出值,如输入相同的x值,都只是返回相同的值y
### 可缓存性(Cacheable)
```
const memoize=function (f) {
const cache = {};
return function () {
const arg = JSON.stringify(arguments);
const res =cache[arg]|| f.apply(f,arguments);
cache.arg= res
return res
}
}
// 需要缓存的函数
const squareNumber = memoize(function(x){ return x*x; });
const square = memoize(squareNumber)
console.log(square(1)); // 1
console.log(square(2)); // 4
console.log(square(1)); // 1
```
延迟执行的方式把**不纯的函数转换为纯函数**
```
var pureHttpCall = memoize(function(url, params){
return function() { return $.getJSON(url, params); }
});
```
- 当调用它的时候才会发请求。这个函数之所以有资格成为纯函数,是因为它总是会根据相同的输入返回相同的输出:给定了`url`和`params`之后,它就只会返回同一个发送 http 请求的函数
- 我们的`memoize`函数工作起来没有任何问题,虽然它缓存的并不是 http 请求所返回的结果,而是生成的函数。
### 可移植性/自文档化(Portable / Self-Documenting)
纯函数是完全自给自足的
```
// 不纯的
var signUp = function(attrs) {
var user = saveUser(attrs);
welcomeUser(user);
};
var saveUser = function(attrs) {
var user = Db.save(attrs);
...
};
var welcomeUser = function(user) {
Email(user, ...);
...
};
// 纯的
var signUp = function(Db, Email, attrs) {
return function() {
var user = saveUser(Db, attrs);
welcomeUser(Email, user);
};
};
var saveUser = function(Db, attrs) {
...
};
var welcomeUser = function(Email, user) {
...
};
```
在 JavaScript 的设定中,可移植性可以意味着把函数序列化(serializing)并通过 socket 发送。也可以意味着代码能够在 web workers 中运行。总之,可移植性是一个非常强大的特性
### 可测试性(Testable)
纯函数让测试更加容易。我们不需要伪造一个“真实的”支付网关,或者每一次测试之前都要配置、之后都要断言状态(assert the state)。只需简单地给函数一个输入,然后断言输出就好了
### 合理性(Reasonable)
很多人相信使用纯函数最大的好处是*引用透明性*(referential transparency
### 并行代码
最后一点,也是**决定性**的一点:我们可以并行运行任意纯函数。因为纯函数根本不需要访问共享的内存,而且根据其定义,纯函数也不会因副作用而进入竞争态(race condition)。
并行代码在服务端 js 环境以及使用了 web worker 的浏览器那里是非常容易实现的,因为它们使用了线程(thread)。不过出于对非纯函数复杂度的考虑,当前主流观点还是避免使用这种并行。
';