React16.8中加入了Hook特性,增强了函数式组件的功能;而Vue3.0引入组合API,让人不禁疑惑为什么主流的前端框架都这么推崇函数式编程?面向对象编程不行了吗?首先,你需要了解函数式编程,一种已经存在很长时间(比OO更久)的编程范式。什么是函数式编程?函数式编程是一种编程范式,主要是用函数来封装计算过程,通过组合各种函数来计算结果。函数式编程的特点1.函数是“一等公民”。所谓“一等公民”,就是函数与其他数据类型处于平等地位,可以赋值给其他变量,也可以作为参数传递给其他变量。一个函数,或者作为另一个函数的返回值。在JavaScript中,函数也是对象,一等公民的地位是应有尽有。比如最常见的setTimeout函数,它的第一个参数是一个函数:setTimeout(()=>{console.log('hello')},1000)2.只用“表达式”而不是“语句”来表达“Formula”(表达式)是一个简单的运算过程,总是有一个返回值;“语句”(statement)是执行某种操作,没有返回值。函数式编程要求只使用表达式,而不是语句。也就是说,每一步都是一个简单的操作,并且都有返回值。3.没有“副作用”所谓“副作用”是指函数内外相互作用,产生除运算以外的其他结果。例如:发送HTTP请求,修改全局变量等。纯函数式编程语言写的函数是没有变量的。因此,只要任何函数的输入是确定的,输出也是确定的。据说这个纯函数没有副作用。并且由于Python、JavaScript等语言允许使用变量,所以它们并不是纯粹的函数式编程语言,只是对函数式编程提供了部分支持。函数式编程强调没有“副作用”,就是说函数要保持独立,返回一个新值,不能有其他行为,尤其是外部变量的值不能被修改。在其他类型的语言中,变量通常用于保存“状态”。变量没有被修改,这意味着状态不能保存在变量中。函数式编程使用参数来保存状态,最好的例子就是递归。由于递归调用会消耗大量内存,尤其是递归深度较深时,容易出现栈溢出。可以通过尾递归进行优化。4.惰性求值惰性求值(也称为按需调用)是一种在将表达式分配给变量(或绑定)时不计算表达式值的技术,该值在变量首次使用时求值。这可以通过避免不必要的评估来提高性能。最常见的例子就是Vue中路由的懒加载:constList=()=>import('@/components/list.vue')//只定义一个函数,不执行import动作constrouter=newVueRouter({routes:[{path:'/list',component:List}]})5.引用透明引用透明是指函数的运行不依赖于外部变量或“状态”,而只依赖于输入参数,任何时候只要参数相同,引用函数得到的返回值总是相同的。在其他类型的语言中,函数的返回值往往与系统状态有关,不同状态下返回值不同。这称为“引用不透明”,很难观察和理解程序的行为。6、无锁并发函数式编程不需要考虑“死锁”(deadlock),因为它不修改变量,所以根本不存在“锁死”线程的问题。你不必担心一个线程的数据被另一个线程修改,所以你可以安全地将工作分配给多个线程并部署“并发”。看下面的代码:lets1=Op1()lets2=Op2()lets3=concat(s1,s2)由于s1和s2互不干扰,不会修改变量,所以谁先执行并不重要,所以大家可以放心添加线程,分配到两个线程上完成。其他类型的语言做不到这一点,因为s1可能会修改系统状态,而s2可能会使用这些状态,所以必须保证s2在s1之后运行,自然不能部署到其他线程。文章开头提到的函数式编程和编程范式:函数式编程是一种编程范式。那么主流的编程范式有哪些呢?共有三种常见的编程范式:命令式编程、面向对象编程和函数式编程。命令式编程命令式编程关注解决问题的步骤,而函数式编程关注数据的映射。例如,现在有这样一个数学表达式:(1+2)*3-4命令式编程可以这样写:leta=1+2;让b=a*3;让c=b-4;function由于公式编程需要用到函数,我们可以将运算过程定义为不同的函数,然后写成如下:letresult=subtract(multiply(add(1,2),3),4)求一个最好的问题描述?抽象(Abstraction)通常是我们用来简化复杂现实世界问题的方法。在面向对象编程中,计算机程序被设计为相互关联的对象。对象是类的实例。它把对象作为程序的基本单位,将程序和数据封装在其中,以提高软件的可重用性、灵活性和可扩展性。对象中的程序可以访问并经常修改与对象关联的数据。对象包含数据(字段、属性)和方法。三种编程范式比较这三种编程范式的特点:命令式编程的核心在于模块化,在实现过程中使用状态,依赖外部变量,容易影响附近代码,可读性差,后期维护成本高也很高;函数式编程的核心是避免副作用,不改变或依赖当前函数之外的数据。结合不可变数据和一等公民函数等特性,函数具有自描述性和高可读性;面向对象编程的核心在于抽象,抽象提供了清晰的对象边界。结合封装、集成、多态,降低代码耦合,提高系统可维护性;JS中的函数式编程如前所述,由于JavaScript允许使用变量,因此它不是一种纯粹的函数式编程语言。但毫无疑问,JS中有很多函数式编程的应用,比如ES5/ES6标准中的箭头函数、迭代器、map、filter、reduce等。在前端框架中,Redux的纯函数,React16.8引入的hooks,Vue3.0的组合API等,也是函数式编程的应用。函数柯里化函数柯里化是指将多元函数转换为顺序调用的单元函数。比如实现一个求和函数:add(1,2,3)//一次接受3个参数,通过函数柯里化处理,变成这样:addCurry(1)(2)(3)//接受一个一次参数通常,我们在实践中使用柯里化来使函数成为单值的,这样可以增加函数的多样性,使其更适用:constreplace=curry((a,b,str)=>str.replace(a,b))//curry是实现柯里化的函数,参数是函数constreplaceSpaceWith=replace(/\s*/)constreplaceSpaceWithComma=replaceSpaceWith(',')constreplaceSpaceWithDash=replaceSpaceWith('-')通过上面的方法,我们可以从一个replace函数中生成很多新的函数,可以用在各种场合。手动实现functioncurrying:functioncurry(fn,...args){returnargs.length
