函数式编程

本篇博客仅以个人学习记录使用
原文链接: https://www.h5jun.com/post/js-functional-1.html

理解

函数式编程这个概念,对于大多数人而言还是有些陌生的,对于它的评价也是褒贬不一,要说到函数式编程就不得不略提一下面向对象。
面向对象对数据进行抽象,将行为以对象的方式封装到数据实体内部,从而降低系统的耦合度。而函数式编程,选择读过程进行抽象,将数据以输入输出流的方式封装进过程内部,从而也降低系统的耦合度。两者虽是截然不同,然而在系统设计的目标上可以说是殊途同归。

面向对象思想和函数式编程思想也是不矛盾的,因为一个庞大的系统,可能既要对数据进行抽象,又要对过程进行抽象,或者一个局部适合进行数据抽象,另一个局部适合进行过程抽象,这都是可能的。数据抽象不一定以对象实体为形式,同样过程抽象也不是说形式上必然是functional的,比如流式对象(InputStream、OutputStream)、Express 的 middleware,就带有明显的过程抽象的特征。但是在通常情况下,OOP更适合用来做数据抽象,FP更适合用来做过程抽象。

纯函数

根据定义,如果一个函数符合两个条件,它被称为纯函数:
1.此函数在相同的输入值时,总是产生相同的输出。函数的输出和当前运行环境的上下文状态无关。
2.此函数运行过程不影响运行环境,比如不会触发事件、更改环境中的对象、终端输出值等。

简单来说,也就是当一个函数的输出不收外部环境影响,同时也不影响外部环境时,该函数就是纯函数。

JavaScript 内置函数中有不少纯函数,也有不少非纯函数。

比如以下函数是纯函数:
1.String.prototype.toUpperCase
2.Array.prototype.map
3.Function.prototype.bind

以下函数不是纯函数:
1.Math.random
2.Date.now
3.document.body.appendChild
4.Array.prototype.sort

为什么要区分纯函数和非纯函数呢?因为在系统里,纯函数与非纯函数相比,在可测试性、可维护性、可移植性、并行计算和可扩展性方面都有着巨大的优势。

对于纯函数,因为是无状态的,测试的时候不需要构建运行时环境,也不需要用特定的顺序进行测试:

1
2
3
4
test( t => {
t.is(add(10,20), 30); //add(x,y) 是个纯函数,不需要为它构建测试环境
...
});

对于非纯函数,就比较复杂:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
test.before(t => {
let list = document.createElement('ul');
list.id = 'xxxxxx';
...
});

test(t => {
let list = document.getElementById('xxxxxx');
t.is(sortList(list).innerHTML, `<ul>
...
</ul>`);
});

test.after(t => {
...
document.removeChild(list);
});

函数式编程能够减少系统中的非纯函数