追求知识本质,不进行知识二次加工。
1、纯函数
pure function 是一种函数,有几个特性:
- 相同输入得到相同输出
- 没有副作用 side effects
- 不依赖于外部状态
比如这个就是一个纯函数:
function add(a, b){
return a + b;
}
比如下面就不是一个纯函数:
// 不满足第一条
function add(a, b){
return a + b + Math.random();
}
// 不满足第二条
function add(a, b){
fetch('/api/getlist')
return a + b;
}
// 不满足第三条
var o = 3;
function add(a, b){
return a + b + o;
}
// 不满足第三条
function add(a, b){
a++
return a + b;
}
再用数组的操作举例:
// slice是纯函数 splice不是纯函数
var xs = [1,2,3,4,5];
// 纯的
xs.slice(0,3);
//=> [1,2,3]
xs.splice(0,3);
//=> [4,5]
2、函数式编程
JS中函数是一等公民,可以当做参数传递给另一个函数。 函数式编程(FP,functional programming),主要有几个特点:
-
- 函数是”一等公民”
-
- 只用”表达式",不用"语句"
-
- 没有”副作用"
-
- 不修改状态
-
- 引用透明(函数运行只靠参数)
函数式编程的好处:
- 代码简洁,开发快速,代码耦合性低
- 接近自然语言,易于理解
- 不依赖状态,方便管理代码
- 易于"并发编程",因为不修改变量,没有"锁"
- 代码热升级。因为没有副作用,相同输入得到相同输出
3、redux简版
比如有个需求,有对象obj,有个属性todos,实现一个功能,让todos进行增加和减少
var obj = {
todos: 0
}
// 根据type觉得进行增加或者减少
var type = 'ADD'
if (type === 'ADD') {
obj.todos++
} else if (type === 'MINUS') {
obj.todos--
}
console.log(obj.todos) // 1
我们将改变的代码封装成函数:
var obj = {
todos: 0
}
// * 这个函数接收两个参数obj和type,此时type决定了obj如何更改。
// * 这个函数不是纯函数,因为它直接改变了传入的参数obj的值。
function change(obj, type) {
if (type === 'ADD') {
obj.todos++
} else if (type === 'MINUS') {
obj.todos--
}
}
change(obj,'ADD')
console.log(obj.todos) // 1
我们现在用纯函数来改变一下
var obj = {
todos: 0
}
function change(obj, type) {
if (type === 'ADD') {
return {
...obj,
todos: obj.todos + 1
}
} else if (type === 'MINUS') {
return {
...obj,
todos: obj.todos - 1
}
}
}
var obj2 = change(obj,'ADD')
console.log(obj1.todos) // 0 不变
console.log(obj2.todos) // 1
现在想增加一些业务,比如,现在希望change函数能够自由的控制每次加多少、减多少
var obj = {
todos: 0
}
function change(obj, action) {
if (action.type === 'ADD') {
return {
...obj,
todos: obj.todos + action.todo
}
} else if (action.type === 'MINUS') {
return {
...obj,
todos: obj.todos - + action.todo
}
}
}
var obj2 = change(obj,{type:'ADD', todo: 10})
console.log(obj1.todos) // 0 不变
console.log(obj2.todos) // 10
上面这个函数非常的有意思,它接收两个参数obj、action,根据action返回一个新的对象,这个对象是从obj“变化”来的。
- 这个change函数我们术语叫做reducer。reducer的英语意思是反应容器。
我们继续抽象。现在我们抽象出一个store,store是Store类的实例,在构建的时候,需要传入reducer。
function Store(reducer) {
// 自己的reducer
this.reducer = reducer;
// 数据,就需要调用一个次reducer,才能得到这个对象
// 因为数据的默认值,是写在reducer里面的形参的默认值的位置上
this.state = this.reducer(undefined,{});
}
Store.proptytope.getState = function(){
return {
...this.state
}
}
Store.proptytope.dispatch = function(action){
this.state = this.reducer(this.state, action);
}
function reducer(state= {"todos" : 0}, action) {
if (action.type === 'ADD') {
return {
...state,
todos: state.todos + action.todo
}
} else if (action.type === 'MINUS') {
return {
...state,
todos: state.todos - + action.todo
}
}
return state
}
// 实例化一个store,不new等于白写。
var store = new Store(reducer);
// 得到值,调用getState()函数,得到里面的todos值
console.log(store.getState().todos); //0
// 唯一能够改变state的方法就是发出dispatch,不发出dispatch不能改变state的值
store.dispatch({"type":"ADD","todo":1});
store.dispatch({"type":"ADD","todo":1});
store.dispatch({"type":"ADD","todo":1});
// 得到值
console.log(store.getState().todos); //3
函数式编程自己没啥用,但是在函数是编程的要求下,就能够有好多工具产生了,比如“可预测状态容器”(predictable state container)。我们刚才写的案例就是可预测状态容器。也就是简版的redux
- 这就是函数式编程的函数,数据、值、变化相互没有关系,是不耦合的,需要有关系的时候,才让他们产生关系,比如下面的语句。
var store = new Store(reducer);
总结
- 1、纯函数的判断方式
- 2、函数式编程的好处
- 3、redux的发展由来