vlambda博客
学习文章列表

一文读懂什么是函数式编程


随着react的流行,函数式编程受到越来越多的关注,加上vue3也开始运用了函数式编程,而且这也是面试官常问的问题。所以学习函数式编程是很有必要的。


那什么是函数式编程呢?它实际运用在哪些场景下呢 相信你看完这篇文章就明白了。




全文导图:

一文读懂什么是函数式编程

 1 
函数式编程的概念


函数式编程可以说一种编程范式,我们常听说的编程范式还有面向过程编程、面向对象编程。这三者有什么区别呢?给大家举个例子。


面向过程编程:按照步骤一步步地实现。


let num1 = 2;let num2 = 3;let sum = num1+num2;console.log(sum);


面向对象编程:把现实世界的事物和事物之间的联系抽象到程序世界中的类和对象,通过封装、继承、多态来演示事物间的联系。


function createPerson (name, age) { this.name = name; this.age = age; this.show = function ({ console.log(`my name is ${this.name}`)    }}const cPerson = new createPerson('php'18);cPerson.show();


函数式编程:是一种映射关系,就像我们数学中的 y=f(x) ;同样的 x ,经过 f(x) 运算得到同样的 y ,是对运算过程的抽象。它有个核心的概念——纯函数。


 2 
函数式编程的核心—纯函数


(一)纯函数有两个条件


a:相同的输入始终会得到相同的输出而且没有任何可观察的副作用。


b:函数内部不会依赖和影响外部的任何变量。


对于条件a,举个例子。


var arr = [1,2,3,4,5]; 
// 非纯函数arr.splice(0,3); //=> [1,2,3] arr.splice(0,3); //=> [4,5] arr.splice(0,3); //=> []
// 纯函数arr.slice(0,3); //=> [1,2,3] arr.slice(0,3); //=> [1,2,3] arr.slice(0,3); //=> [1,2,3]   


从上面的例子里看出,对于slice来说,它对于相同的输入总能返回相同的输出;而splice直接在原数组上作出改变,产生了可观察到的副作用,即改变了数组。


对于条件b:函数内部不会依赖和影响外部的任何变量。


// 非纯函数let mini = 18;function checkAge1(age) { return age >= mini}console.log(checkAge1(20))
// 纯函数function checkAge2(age) { let mini = 18; return age >= mini}console.log(checkAge2(20))


在 checkAge1 函数中依赖全局变量mini,一旦mini被改变,输出结果就会改变。而 checkAge2 中,无论任何时候,传递相同的参数 age ,结果都不会被影响。


(二)纯函数代表——Lodash



  1. 安装

    npm i lodash


  2. 引入

    const _=require('lodash');const array=['jack','php','gb','luck'];console.log(_.first(array));//jackconsole.log(_.last(array));//luck


(三)纯函数优势


由于纯函数的相同输入有相同输出和不被外部环境所影响两个特点,给函数式编程带来了三大好处:可缓存、可测试、并行处理。


a:可缓存


因为纯函数对相同的输入始终有相同的结果,所以可以把纯函数的结果缓存起来。


b:可测试


纯函数让测试更方便


c:并行处理


  • 在多线程环境下并行操作共享的内存数据很可能会出现意外情况


  • 纯函数不需要访问共享的内存数据,所以在并行环境下可以任意运行纯函数


 3 
函数式编程的用法


(一)高阶函数


所谓高阶函数就是函数作为参数或者返回值是函数的函数。例如:


  1. 函数作为参数


    例如模拟js中的map函数。


    const map = (array,fn)=>{ let results = []; for(let value of array){ results.push(fn(value)); } return results;}//测试let arr = [1,2,3,4,5];arr = map(arr,v=>v*v);console.log(arr)//[ 1, 4, 9, 16, 25 ]


    以上就是把fn函数作为参数传递进去。类似的js中的forEach、filter,every、some等都可以把函数作为参数传递进去,所以都是高阶函数。


    let arr = [1,2,3,4,5];arr.forEach(function(i)=>{ });arr.filter(function(i)=>{  return i>4;});


  2. 返回值是函数


    function makeFn(){ let msg='hello function'; return function(){ console.log(msg) }}const fn = makeFn();fn();



(二)柯里化


函数柯里化就是当函数有多个参数,可以对这个函数进行改造,只传递部分参数。并且让这个函数返回一个新的函数,新的函数再接受剩余的参数并且返回相应的结果。例如判断一个字符串是否含有数字:


function haveMatch(reg) { return function (str) { return str.match(reg); }}
const haveNum = haveMatch(/\d+/g);
console.log(haveNum('aa22'));//['22']


在lodash中提供了一个通用的柯里化方法——curry


curry:本身是个纯函数,传进去的参数是纯函数,那返回的也是纯函数。例如以上柯里化函数经过 curry 改造。


const _ = require('lodash');const curried = _.curry((reg,str)=>{ return str.match(reg);});const haveNum = curried(/\d+/g);console.log(haveNum('aa22'));//['22']


以上如果传递 haveMatch 所需的部分参数,那么会返回一个函数,等待继续传递参数。直到传递所有参数,返回最终结果。使用 curry 的目的是把多元函数最终转化为一元函数。


(三)函数组合


如果一个函数要经过多个函数处理才能得到最终值,这个时候可以把中间过程的函数合并成一个函数。就像是数据的管道,函数组合就是把这些管道连接起来,让数据穿过多个管道最终结果。函数组合默认从右到左执行。



fn = compose(f1,f2,f3);b = fn(a);


现在做一个函数组合演示,假如现在有个需求:将数组做三步处理,(1)转化为大写字母;(2)颠倒顺序排列;(3)取出第一个数组


function toUpper(array){ return array.map(item=>item.toUpperCase());}function reverse(array){ return array.reverse();}function first(array){ return array[0]}
//函数组合演示function compose(a,b,c){ return function(arr){ return a(b(c(arr))) }}
const comFn = compose(first,reverse,toUpper);const result = comFn(['php','gb','pwb'])console.log(result);//PWB


以上就是将每一步都封装成一个函数,然后将多个函数重新组合成一个新函数,得到最终结果。这样划分多个粒度的函数可以任意组合成我们所需要的函数。例如,将数组转化为大写,然后取出第一个数组。


const comFn = compose(first,toUpper);const result = comFn(['php','gb','pwb']);//PHP


在lodash中提供了函数组合方法——flowRight


const _ = require('lodash');const toUpper = item => item.toUpperCase();const reverse = arr => arr.reverse();const first = arr => arr[0];const f = _.flowRight(toUpper, first, reverse);console.log(f(['php','gb','pwb']));//PWB


最后的话:


函数式编程是一种编程思想,想要掌握一种编程思想是需要很长时间的,可能是半年,可能更长时间。


虽然在编码中不需要全部使用函数式编程的思想,但是我们可以尽量多掌握一些编程思想,这样在实际工作中就可以从多方面比较,找出相对更高效的编码方式。


以上完,希望对你有所帮助。


往期精选:





END