一.纯函数
定义:
纯函数是指在相同的输入下总是返回相同的输出,且没有副作用(不修改参数,不修改全局变量等)的函数。
特点:
- 相同的输入总是返回相同的输出
- 没有副作用
- 可缓存,提高代码性能(在遇到大运算代码时可以将结果缓存,如果后面入参一样可以直接使用结果)
- 易于进行代码测试
优点:
- 提高代码的可读性和可维护性
- 可以避免一些常见问题,如并发问题和数据共享问题
- 使代码更加模块化
应用场景:
- 数组的转换和过滤
- 数组和对象的操作
- 纯函数可以作为其他函数的参数,如高阶函数的参数
例子:
function add(a,b){
return a+b;
}
function multiply(a,b){
return a*b;
}
function filter(arr,fn){
return arr.filter(fn);
}
function compose(f,g){
return function(x){
return f(g(x));
};
}
二. 副作用
定义:
函数除了计算之外,还对它的执行上下文和执行宿主等外部环境造成了一些其他的影响,这些影响就是副作用。比如函数中做请求、改变全局上下文中的某个变量等等
三. 一等公民函数
定义:
- 可以被当作参数传递给其他函数
- 可以赋值给变量
- 可以作为另外一个函数的返回值
四.不可变数据
不可变数据是保障函数纯度的安全线。因为纯函数要求同样的输出会对应着同样的输入,如果出现如下的情况则改函数就不再纯
const param = {width: 100, height: 100};
function calcArea(obj) {
return obj.width * obj.height
}
console.log(calcArea(param)) // 10000
const params = param
params.height = 10
console.log(param === params) // true
console.log(calcArea(param)) // 1000
可以看到params经过param赋值后,然后又改变了height的值,但是这个时候params === param
仍然为true
,但是计算的结果却不一样了,这里涉及到了js数据类型的值类型
和引用类型
,值类型
的拷贝是在内存中重新开辟一块区域来保存数据,而引用类型
的拷贝则是将指向数据的地址拷贝了一份,但是其指向还是没有变化的。导致修改拷贝之后的引用之后,内存中保存的数据也会改变,这就导致了数据的可变性。
可变数据的危害:
将函数的计算结果变得难以预测(说不定就在程序中的某个地方被改变了),这就违背了纯函数的定义
解决办法:
控制数据的变化,确保所有的变化都在可预期的范围内发生。可以使用拷贝来复制一份数据,有对数据操作的计算可以在拷贝的数据上进行,这样就不会对原有的数据造成影响,从而不会影响其他函数的纯度
五.持久化数据结构
虽然拷贝是解决数据可变的一种方案,但是在有些情况下并不适用,比如当拷贝的数据太大以及用的地方很多时,这个时候多次拷贝数据就会产生冗余,占用更多的内存。这种情况下拷贝并不是一个好的方案。
5.1 ImmerJs
5.2 ImmutableJs
immutable是FaceBook团队是一个实现数据持久化机构的库。 他的原理是共享未改变的数据,已修改的数据重新创建一块区域来保存 ,这样就避免了重复拷贝数据的冗余问题
使用例子:
// 引入 immutable 库里的 Map 对象,它用于创建对象
import { Map } from 'immutable'
// 初始化一个对象 baseMap
const originData = Map({ name: 'xiuyan', hobby: 'coding', age: 666 })
// 使用 immutable 暴露的 Api 来修改 baseMap 的内容
const mutatedData = originData.set({ age: 66.6 })
// 我们会发现修改 baseMap 后将会返回一个新的对象,这个对象的引用和 baseMap 是不同的
console.log('originData === mutatedData', originData === mutatedData)