函数式编程
Chunbin Lv4

函数式编程的思想

函数式编程是一种编程范式,它更关注从输入到输出的映射结果的编程思想,他有以下一些思想

函数是一等公民

我的理解就是能用函数就用函数,函数可以做参数、变量、返回值.
举个栗子,比如我们要对数组进行排序

1
2
3
4
5
6
7
8
9
10
11
12
const arr = [1,3,4,2,1]

// 例1:不够好
arr.sort((a,b)=>{
return a - b
})

// 例2:好
function compare(a,b){
return a - b
}
arr.sort(compare)

为什么方法2比较好?
从复用性的角度来说,当我们很多地方需要使用同样的方法进行排序,那么我们直接引入compare方法即可。

1
2
3
import {compare} from '@/utils'
const arr2 = [1,2,3,4]
arr2.sort(compare)

从维护性的角度来说,当我们的需求变动,我们只需要修改compare里面即可,或者使用别的compare方法。
比如换个需求变动,更换排序方式为倒置,我们只要很方便的修改sort的入参即可

1
2
3
4
5
// 倒置数组
function reverse(){
return -1
}
arr.sort(reverse)

在举个参数变化的例子:

1
2
3
4
5
// bad
httpGet('/post/2', json => renderPost(json));

// good
httpGet('/post/2', renderPost);

为什么呢,比如这个时候,我们需要renderPost里面对错误进行处理,

1
2
3
4
5
// 胶水函数都需要改
httpGet('/post/2', (json, err) => renderPost(json, err));

// 而下面的不需要修改
httpGet('/post/2', renderPost);

可以看到我们少改动了一些代码

纯函数

这是一个数学概念,f(x) = y,纯函数指 x 输入一致,那么 y 也一致。
举个例子:

1
2
3
4
5
6
7
8
9
10
11
// 纯函数
const pureFn = (x) => {
let i = 1;
return x + i;
};

// 不是纯函数
let i = 1;
const notPureFn = (x) => {
return x + i;
};

例子 1 是纯函数,因为当 x 一致,返回永远;
例子 2 不是纯函数,notPureFn依赖了外部变量i,如果这个时候程序外部改变了 i 那么返回的结果就不一致;

纯函数的优点

我们从函数的定义可以不难得出纯函数的几个优点

  1. 易于单元测试
1
2
3
4
5
6
7
function sum(a, b) {
return a + b;
}

test("adds 1 + 2 to equal 3", () => {
expect(sum(1, 2)).toBe(3);
});

因为纯函数不依赖外部环境,我们可以方便的写出单元测试,不需要为单元测试增加外部环境。

  1. 易于重构
    因为可以很方便的增加单元测试,重构过程中可以减少很多理解成本

  2. bug 可控
    因为纯函数不影响外部环境,产生 bug 影响面小。

  3. 可缓存
    因为输入一致,输出也是一致,所以我们可以对此进行缓存。

如何写代码

我们应该要多写纯函数,少写非纯函数

副作用

副作用指函数内部和外部的交互,比如:

  • 发起 http 请求
  • 使用外部变量
  • 数据库读写
    等等

函数式编程要求我们通过拆分,减少副作用函数

组合(compose)

我们可以把函数组合起来,生成一个新的函数,比如g(f(x))或者g.a().b(),但g.a().b()需要放在原型链上,更多的时候我们采用的式g(f(x))这种方式

1
2
3
4
5
6
7
8
const data = [1, 2, 3, 4]
const square = arr => arr.map(n => n * n)
const sum = arr => arr.reduce((cur, next) => cur + next, 0)
sum(square(data)) // => 30
// 组合
const compose = (f, g) => x => f(g(x))
const sumOfSquare = compose(sum, square)
sumOfSquare(data) // => 30

值得注意的是,在这个例子中,sum和square的顺序是不能交换的, 但是有些时候, 组合是可以交换位置的。
最后再写一个通用的compose函数

1
2
3
4
5
6
7
function compose(...fns) {
return function (x, ...extra) {
return fns.reduceRight((arg, fn) => {
return fn(arg, ...extra);
}, x);
};
}

科里化(curry)

通用科里化函数

1
2
3
4
5
6
7
8
9
function curry(fn,...argsOut) {
return function (...args) {
const newArgs = argsOut.concat(args)
if (fn.length <= newArgs.length) {
return fn.call(null, ...newArgs);
}
return curry(fn,...newArgs);
};
}

推荐文章

函数编程指北

 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep
访客数 访问量