函数式编程是React的精髓,在正式讲解React之前,有必要先了解一下函数式编程,有助于更好的理解React的特点。函数式编程(Functional Programming)不是一种新的框架或工具,而是一种以函数为主的编程范式。编程范式也叫编程范型,是一类编程风格,除了函数式编程,常用的还有面向对象编程、命令式编程等。

一、声明式编程

  声明时编程也是一种范式,但它是一个比较大的概念,函数式编程是它的一个子集。声明式编程能指定每一步操作,而不用向计算机描述具体的实现细节。与之相对立的是命令式编程,它会命令计算机每一步该怎么做。以数组的元素翻倍为例,先用命令式编程实现,如下所示。

var arr = [1, 2, 3],
length = arr.length,
doubles = [];
for (let i = 0; i < length; i++) {
doubles.push(arr[i] * 2);
}

  在命令式的代码中,先用for循环遍历整个数组,然后让每个元素乘以二,再将计算结果插入到doubles数组中,直至将所有的元素计算完才终止整套操作。改用声明式编程可以像下面这样实现相同的功能。

var doubles = [1, 2, 3].map(value => value * 2);

  在声明式的代码中,用map()方法替代了循环语句(即不指明流程的控制方式),既不用再维护计数器,也不用再通过索引访问数组的元素,配合ES6的箭头函数让整套操作变得非常简洁。

  除了这些表面区别之外,还有个最本质的区别,那就是声明式编程会避免用变量保存程序的状态,从而能提高代码的无状态性。在命令式的代码中,每次迭代都会修改doubles变量,这是个状态变量,而在声明式的代码中,改用返回值保存程序的状态。

二、函数优先

  函数式编程强调在程序中使用函数。由于JavaScript中的函数是一等公民,它既可以是变量的值,也可以作为另一函数的参数或返回值,因此通过函数可构建一层抽象以替代流程控制或解决复杂的逻辑操作。例如对数组中的数字进行排序和过滤,可以像下面这样运用函数式编程的思想实现。

[4, 1, 5, 2, 3].sort((a, b) => a > b).filter(value => value > 2);        //[3, 4, 5]

  函数式编程旨在将复杂的运算分解成一系列嵌套的函数,逐层推导,不断渐进,直至完成运算。

三、纯函数

  纯函数(Pure Function)是一种没有副作用、引用透明的函数,它是函数式编程的基本概念,接下来会重点讲解它的三个特征。

1)函数的副作用

  函数在读写外部资源或执行不确定的操作时就会产生副作用,例如修改函数外的变量、调用Date.now()或Math.random()、更新cookie信息等。副作用不仅会降低程序整体的可读性,有时候还会带来意料之外、难以排查的错误,下面是一个副作用的例子。

var digit = 1;
function increment() {
digit += Math.random();
return digit;
}

  在上面的代码中,increment()函数产生了副作用,因为每次调用它都会更新外部的digit变量,并且每次得到的计算结果也无法预知。

2)引用透明

  如果传递给函数相同的参数,始终能得到相同的结果,那么就能说这个函数是引用透明(Referential Transparent)的。简单的说就是,函数的运行只受其输入值的影响,如下代码所示,传递给add()函数固定的参数会返回固定的值。

function add(a, b) {
return a + b;
}

3)参数值不可变

  传递给纯函数的参数值是不允许在内部将其改变的,换句话说,在函数内部使用的是参数值的副本。如果参数值是基本类型的,那么传递给函数的就是其副本;但如果参数值是对象类型的,那么需要注意,传递给函数的是引用对象的指针。

  下面用一个示例说明,addDigit()函数的参数是一个数组,它的功能是为数组的每个元素加一,在执行addDigit(digits)之后,由于digits变量是一个数组,因此它的元素会随着函数的调用而被改变。

var digits = [1, 2, 3];
function addDigit(arr) {
for (let i = 0, len = arr.length; i < len; i++) {
arr[i] += 1;
}
return arr;
}
addDigit(digits);
console.log(digits); //[2, 3, 4]

  接下来修改addDigit()函数,使之能满足纯函数的要求,如下所示。

var digits = [1, 2, 3];
function addDigit(arr) {
return arr.map(value => value + 1);
}
addDigit(digits);
console.log(digits); //[1, 2, 3]

  在addDigit()函数内部,用map()方法替代for循环,使得在不改变参数的前提下,完成元素加一的功能。

四、优点

  函数式编程有许多优点,本节只列出了其中的两点。

  (1)函数式编程可将复杂的任务分解成一个个既简单又独立的纯函数,有利于提高代码的模块化、复用性、预测性以及可测试性。

  (2)函数式编程有很高的自由度,可以采用更符合人类思维习惯的链式写法,以此提高代码的可读性。

  接下来会用两种函数式的写法操作一个数组,为了便于演示省略了函数的具体实现,首先是普通的函数式写法,如下所示。

elementDouble(filterEven(arr, filterFn), doubleFn);

  两个函数都有两个参数,第一个是数组,第二个是相应的回调函数。具体的执行过程是先通过filterEven()函数过滤掉数组中偶数位置的元素,再用elementDouble()函数把每个元素翻倍,下面改成链式的写法。

filerEven(arr, filterFn).elementDouble(arr, doubleFn);

  通过两段代码的对比可以看出,链式的写法更容易让人理解,代码意图也更清晰。

React躬行记(1)——函数式编程的更多相关文章

  1. React躬行记(10)——高阶组件

    高阶组件(High Order Component,简称HOC)不是一个真的组件,而是一个没有副作用的纯函数,以组件作为参数,返回一个功能增强的新组件,在很多第三方库(例如Redux.Relay等)中 ...

  2. React躬行记(11)——Redux基础

    Redux是一个可预测的状态容器,不但融合了函数式编程思想,还严格遵循了单向数据流的理念.Redux继承了Flux的架构思想,并在此基础上进行了精简.优化和扩展,力求用最少的API完成最主要的功能,它 ...

  3. React躬行记(12)——Redux中间件

    Redux的中间件(Middleware)遵循了即插即用的设计思想,出现在Action到达Reducer之前(如图10所示)的位置.中间件是一个固定模式的独立函数,当把多个中间件像管道那样串联在一起时 ...

  4. React躬行记(8)——样式

    由于React推崇组件模式,因此会要求HTML.CSS和JavaScript混合在一起,虽然这与过去的关注点分离正好相反,但是更有利于组件之间的隔离.React已将HTML用JSX封装,而对CSS只进 ...

  5. React躬行记(5)——React和DOM

    React实现了一套与浏览器无关的DOM系统,包括元素渲染.节点查询.事件处理等机制. 一.ReactDOM 自React v0.14开始,官方将与DOM相关的操作从React中剥离,组成单独的rea ...

  6. React躬行记(3)——组件

    组件(Component)由若干个React元素组成,包含属性.状态和生命周期等部分,满足独立.可复用.高内聚和低耦合等设计原则,每个React应用程序都是由一个个的组件搭建而成,即组成React应用 ...

  7. React躬行记(2)——JSX

    JSX既不是字符串,也不是HTML,而是一种类似XML,用于描述用户界面的JavaScript扩展语法,如下代码所示.在使用JSX时,为了避免自动插入分号时出现问题,推荐在其最外层用圆括号包裹,并且必 ...

  8. React躬行记(4)——生命周期

    组件的生命周期(Life Cycle)包含三个阶段:挂载(Mounting).更新(Updating)和卸载(Unmounting),在每个阶段都会有相应的回调方法(也叫钩子)可供选择,从而能更好的控 ...

  9. React躬行记(6)——事件

    React在原生事件的基础上,重新设计了一套跨浏览器的合成事件(SyntheticEvent),在事件传播.注册方式.事件对象等多个方面都做了特别的处理. 一.注册事件 合成事件采用声明式的注册方式, ...

随机推荐

  1. java基础篇一

    引言 本人系南京一小小学校的大三小小菜鸟,三年来学了很多杂七杂八的,也荒废了大量的时间,马上就要秋招了,之前也看了不少面试题,备选了一些简单的项目,看了不知多少本的几百页厚的各种知识的pdf电子书,发 ...

  2. windows10查看电脑已经保存的wifi密码

    1,打开windows的命令窗口,输入    netsh wlan show profiles,如下图,这个命令仅仅只是查看一下电脑保存的所有的wifi名字 2,需要查看密码的话,则需要输入这个命令, ...

  3. MySQL的安装、启动和基础配置 —— mac版本

    安装 第一步:打开网址,https://www.mysql.com,点击downloads之后跳转到https://www.mysql.com/downloads/选择Community选项 第二步: ...

  4. 函数式编程 -> Lambda

    一.函数式编程 函数式编程,同面向对象编程.指令式编程一样,是一种软件编程范式,在多种编程语言中都有应用.百科词条中有很学术化的解释,但理解起来并不容易.不过,我们可以借助于数学中函数的概念,来理解函 ...

  5. vue中点击屏幕其他区域关闭自定义div弹出框

    直接上代码: mounted: function () { let that = this; $(document).on('click', function (e) { let dom = $('. ...

  6. 微服务分布式 spring cloud springboot 框架源码 activiti工作流 前后分离

    1.代码生成器: [正反双向](单表.主表.明细表.树形表,快速开发利器)freemaker模版技术 ,0个代码不用写,生成完整的一个模块,带页面.建表sql脚本.处理类.service等完整模块2. ...

  7. 因特尔CPU上TM和R标识的区别

    TM是英文trademark的缩写,TM标志并非对商标起到保护作用,它与R不同,TM表示的是该商标已经向国家商标局提出申请,并且国家商标局也已经下发了<受理通知书>,进入了异议期,这样就可 ...

  8. CentOS 7上的主机名设置和基本网络管理

    主机名 CentOS 6 查看. # hostname 设置. # hostname NEW_NAME 设置完成后,xshell的会话中不会显示NEW_NAME,可通过重新登录会话来显示.不过实际上我 ...

  9. openldap数据双向同步

    配置双主复制功能,在主1和主2上执行均下面的步骤 vim syncprov_mod.ldif dn: cn=module,cn=configobjectClass: olcModuleListcn: ...

  10. 修改HTML元素

    通过HTML DOM,JavaScript能够访问并修改HTML文档中的每个元素 修改元素文本内容 document.getElementById("p1").innerHTML= ...