Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

纯函数、函数式编程、柯里化 #136

Open
TieMuZhen opened this issue Mar 19, 2022 · 0 comments
Open

纯函数、函数式编程、柯里化 #136

TieMuZhen opened this issue Mar 19, 2022 · 0 comments

Comments

@TieMuZhen
Copy link
Owner

一、纯函数

前言

纯函数是函数式编程的基础

纯函数的概念

首先我们来看看纯函数的基本概念:

相同的输入,总是会的到相同的输出,并且在执行过程中没有任何副作用。

该怎么去理解上面的概念呢?我们要把上面这句话拆成两部分来看。

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));

关于柯里化函数的意义如下:

  • 让纯函数更纯,每次接受一个参数,松散解耦
  • 惰性执行
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant