-
Notifications
You must be signed in to change notification settings - Fork 0
Description
一、纯函数
前言
纯函数是函数式编程的基础
纯函数的概念
首先我们来看看纯函数的基本概念:
相同的输入,总是会的到相同的输出,并且在执行过程中没有任何副作用。
该怎么去理解上面的概念呢?我们要把上面这句话拆成两部分来看。
1、相同的输入,总是会得到相同的输出。
来看看下面的例子:
let a = 1;
function xAdd(x) {
return x + a;
};
xAdd(1); //2
上面这个函数就不是一个纯函数,因为在我们程序执行的过程中,变量a很可能会发生改变,当变量a发生改变时,我们同样执行xAdd(1)
时得到的输出也就不同了。
再看另一个例子:
function sum(x, y) {
return x + y;
};
sum(1,2); //3
在这个例子中,符合相同的输入得到相同的输出这个概念,sum是一个纯函数。
2、执行过程中没有任何副作用
这里我们要搞清楚什么是副作用,这里的副作用指的是函数在执行过程中产生了外部可观察变化。
- 发起HTTP请求
- 操作DOM
- 修改外部数据
- console.log()打印数据
- 调用Date.now()或者Math.random()
上面一系列操作都可以被称为是副作用。下面可以接着看一个修改外部数据从而产生副作用的例子:
let a = 1;
function func() {
a = 'b';
};
func();
console.log(a); // b
我们运行了func
函数,外部的变量a
的值发生了改变,这就是产生了所谓的副作用,所以func
不是一个纯函数。当我们这样进行修改:
function func2() {
let a = 1;
a = 'a';
return a
};
func(); // a
函数fun2
不会对产生外部可观察变化,也就不会产生副作用,它就是一个纯函数。
一个纯函数,上面所说的两个条件缺一不可。
纯函数的好处
通过了解纯函数的概念,我相信有的小伙伴已经能感觉到纯函数的一些的好处了:
- 更容易进行测试,结果只依赖输入,测试时可以确保输出稳定
- 更容易维护和重构,我们可以写出质量更高的代码
- 更容易调用,我们不用担心函数会有什么副作用
- 结果可以缓存,因为相同的输入总是会得到相同的输出
纯函数运用的经典案例
Lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库。我相信很多小伙伴也经常用到吧,这也是纯函数代表。
合理运用纯函数编写公共方法
假设我们要编写一个把数组中的小写字母转为大写字母的公共方法:
let upperCaseLists = (value) => {
return value.map((item) => item.toUpperCase())
}
纯函数组件的缺点
没有生命周期。
二、函数式编程
函数式编程好处
1. 代码简洁,开发快速
函数式编程大量使用函数,减少了代码的重复,因此程序比较短,开发速度较快。
2. 接近自然语言,易于理解
函数式编程的自由度很高,可以写出很接近自然语言的代码。
前文曾经将表达式(1 + 2) * 3 - 4
,写成函数式语言:
subtract(multiply(add(1,2), 3), 4)
对它进行变形,不难得到另一种写法:
add(1,2).multiply(3).subtract(4)
这基本就是自然语言的表达了。再看下面的代码,大家应该一眼就能明白它的意思吧:
merge([1,2],[3,4]).sort().search("2")
因此,函数式编程的代码更容易理解。
3. 更方便的代码管理
函数式编程不依赖、也不会改变外界的状态,只要给定输入参数,返回的结果必定相同。因此,每一个函数都可以被看做独立单元,很有利于进行单元测试(unit testing)和除错(debugging),以及模块化组合。
4. 易于"并发编程"
函数式编程不需要考虑"死锁"(deadlock),因为它不修改变量,所以根本不存在"锁"线程的问题。不必担心一个线程的数据,被另一个线程修改,所以可以很放心地把工作分摊到多个线程,部署"并发编程"(concurrency)。
5. 代码的热升级
函数式编程没有副作用,只要保证接口不变,内部实现是外部无关的。所以,可以在运行状态下直接升级代码,不需要重启,也不需要停机。
函数式编程缺点
- 性能:函数式编程相对于指令式编程,性能绝对是一个短板,因为它往往会对一个方法进行过度包装,从而产生上下文切换的性能开销
- 递归陷阱:在函数式编程中,为了实现迭代,通常会采用递归操作
三、柯里化
柯里化是把一个多参数函数转化成一个嵌套的一元函数的过程
一个二元函数如下:
let fn = (x,y)=>x+y;
转化成柯里化函数如下:
const curry = function(fn){
return function(x){
return function(y){
return fn(x,y);
}
}
}
let myfn = curry(fn);
console.log( myfn(1)(2) );
上面的curry
函数只能处理二元情况,下面再来实现一个实现多参数的情况
// 多参数柯里化;
const curry = function(fn){
return function curriedFn(...args){
if(args.length<fn.length){
return function(){
return curriedFn(...args.concat([...arguments]));
}
}
return fn(...args);
}
}
const fn = (x,y,z,a)=>x+y+z+a;
const myfn = curry(fn);
console.log(myfn(1)(2)(3)(1));
关于柯里化函数的意义如下:
- 让纯函数更纯,每次接受一个参数,松散解耦
- 惰性执行