浅谈函数式编程(Functional Programing)

559 阅读4分钟

简述

今天来说说Functional Programing,最近听得比较多functional,经常会听到,你这里写得比较functional,你这里写得不够Functional,我心里出现许多疑问,什么是Functional?Functional有什么优点?什么样的代码才算Functional?

Functional Programing,中文意思是函数式编程,是编程范式里面的一种。

与面相过程、面相对象代码相比,函数式编程往往更加简洁、更加可预测,并且容易测试。

历史

函数式编程(1930)其实是个比面相对象编程(1960)更古老的东西,它比面相对象概念还要早30年提出,为什么近些年突然又会火起来呢?

integer-perf.png

最主要是现在的CPU发展遇到了瓶颈,CPU的单核性能不像早十几年那样,每两年翻一倍,若要提高算力,基本上只能靠核心数去叠加,获取更高的算力。

用到多核心的时候,必不可少用到并行程序,才能大大发挥多核心的效率,但是并行程序的稳定正确运行又是另外一个问题,如果处理不好,很容易出现各种锁,各种同步的问题。

这时候函数式编程映入眼帘,因为函数式编程拥有引用透明、不变性、没有副作用等特性,在并行处理上比较有优势,所以近些年又火起来了。

概念

纯函数

特征:

  • 相同的输入,总是返回相同的输出

    有时候也称引用透明性幂等性,使用相同参数多次调用纯函数总是返回一样的结果。

  • 不会产生副作用

    调用函数时,除了返回函数值外,不会产生额外的影响,例如I/O、修改参数、修改函数外的变量。

// 纯
const increaseCounter = (value) => {
  return value + 1;
}
console.log(increaseCounter(1)) // 2

// 不纯
const number = 1;
const increaseCounter = (value) => {
  return value + number;
}
console.log(increaseCounter(1)) // 2

优点:

  • 缓存优化

    因为具有幂等性,相同参数多次调用函数结果都是一样,可以使用缓存来保存已经运行过的结果

  • 方便测试

    因为纯函数不会依赖函数外的值和不会修改函数外的值,不需要考虑函数外的情况,方便进行测试。

高阶函数

特征:

  • 参数是函数或者输出结果是函数

高阶函数在 JavaScript 中被广泛使用,其实我们日常开发中也会常常用到,只是以前不知道那叫高阶函数。

例如在ES5引入的map、filter对象方法,他们都是高阶函数的好例子,它们可以把其他函数作为参数,然后返回一个新的函数让我们去使用。

我们尝试一下把numbers数组里面的元素都加一,分别使用高阶函数和不用高阶函数实现一次,看看他们的区别。

// 不使用map高阶函数
const numbers = [1, 2, 3];
let newNumbers = [];
for (let index = 0; index < numbers.length; index++) {
  newNumbers.push(numbers[index] + 1);
}
console.log(newNumbers); // (3) [2, 3, 4]

// 使用map高阶函数
const numbers = [1,2,3];
const newNumbers = numbers.map((number)=> number + 1);
console.log(newNumbers); // (3) [2, 3, 4]

可以看到上述例子,使用高阶函数可以让我们的代码更加简洁、可读性更高,而且还可以让代码复用。

不可变性

不可变性是指创建后的变量不能被修改,相反,可变性是指创建后的变量可以被修改。

在函数式编程中,不可变性是一个核心的原则,不变到什么地步呢?在函数式编程的原教中连普通的循环都不可以写,因为循环会有index或者条件之类的变量,必须使用递归去实现,去消除这些可变的状态。

// 可变性
const users = ['tom', 'eddy'];
users.push('ben');
console.log(users); // [ 'tom', 'eddy', 'ben' ]


// 不可变性
const users = ['tom', 'eddy'];
const newUsers = [...users, 'ben'];
console.log(newUsers); // [ 'tom', 'eddy', 'ben' ]

优点

  • 提高可读性

    因为变量保持不变,代码更容易理解和推理

  • 提高维护性

    虽然编写不可变性代码可能需要更多的思考和考虑,但是从长远来说,可维护性更高

  • 可追溯性

    轻松跟踪代码中数据的更改方式和位置,并确定每次更改时需要重新加载应用程序的哪些部分

  • 更容易测试和调试

    由于不可变对象的状态不会改变,所以测试代码和跟踪错误要容易得多

参考资料

en.wikipedia.org/wiki/Functi…

en.wikipedia.org/wiki/Object…

www.zhihu.com/question/19…

www.zhihu.com/question/30…

preshing.com/20120208/a-…

medium.com/javascript-…