React躬行记(1)——函数式编程
函数式编程是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)——函数式编程的更多相关文章
- React躬行记(10)——高阶组件
高阶组件(High Order Component,简称HOC)不是一个真的组件,而是一个没有副作用的纯函数,以组件作为参数,返回一个功能增强的新组件,在很多第三方库(例如Redux.Relay等)中 ...
- React躬行记(11)——Redux基础
Redux是一个可预测的状态容器,不但融合了函数式编程思想,还严格遵循了单向数据流的理念.Redux继承了Flux的架构思想,并在此基础上进行了精简.优化和扩展,力求用最少的API完成最主要的功能,它 ...
- React躬行记(12)——Redux中间件
Redux的中间件(Middleware)遵循了即插即用的设计思想,出现在Action到达Reducer之前(如图10所示)的位置.中间件是一个固定模式的独立函数,当把多个中间件像管道那样串联在一起时 ...
- React躬行记(8)——样式
由于React推崇组件模式,因此会要求HTML.CSS和JavaScript混合在一起,虽然这与过去的关注点分离正好相反,但是更有利于组件之间的隔离.React已将HTML用JSX封装,而对CSS只进 ...
- React躬行记(5)——React和DOM
React实现了一套与浏览器无关的DOM系统,包括元素渲染.节点查询.事件处理等机制. 一.ReactDOM 自React v0.14开始,官方将与DOM相关的操作从React中剥离,组成单独的rea ...
- React躬行记(3)——组件
组件(Component)由若干个React元素组成,包含属性.状态和生命周期等部分,满足独立.可复用.高内聚和低耦合等设计原则,每个React应用程序都是由一个个的组件搭建而成,即组成React应用 ...
- React躬行记(2)——JSX
JSX既不是字符串,也不是HTML,而是一种类似XML,用于描述用户界面的JavaScript扩展语法,如下代码所示.在使用JSX时,为了避免自动插入分号时出现问题,推荐在其最外层用圆括号包裹,并且必 ...
- React躬行记(4)——生命周期
组件的生命周期(Life Cycle)包含三个阶段:挂载(Mounting).更新(Updating)和卸载(Unmounting),在每个阶段都会有相应的回调方法(也叫钩子)可供选择,从而能更好的控 ...
- React躬行记(6)——事件
React在原生事件的基础上,重新设计了一套跨浏览器的合成事件(SyntheticEvent),在事件传播.注册方式.事件对象等多个方面都做了特别的处理. 一.注册事件 合成事件采用声明式的注册方式, ...
随机推荐
- java面试题干货126-170
这部分主要是开源Java EE框架方面的内容,包括Hibernate.MyBatis.Spring.Spring MVC等,由于Struts 2已经是明日黄花,在这里就不讨论Struts 2的面试题, ...
- CCF-CSP题解 201503-4 网络延时
求树的直径. 两遍\(dfs\)就好了. #include <cstdio> #include <cstring> #include <algorithm> #in ...
- 2016/09/29 Maven简介
1.1 何为Maven Maven这个词可以翻译为"知识的积累",也可以翻译为"专家"或"内行".作为Apache组织中的一个颇为成功的开源 ...
- 《Java练习题》习题集一
编程合集: https://www.cnblogs.com/jssj/p/12002760.html Java总结:https://www.cnblogs.com/jssj/p/11146205.ht ...
- 如何下载Vimeo视频
MediaHuman YouTube Downloader是应用在Mac上的一款非常优秀的YouTube视频下载工具,YouTube Downloader破解版将帮助你快速完成视频下载,而不会挂断.您 ...
- Vue+Webpack之 代码及打包优化
本文重点介绍Vue单页面应用的优化手段: 异步加载 面切换时加loading特效 点击延迟 inline manifest 逻辑代码优化 依赖包体积优化 cdn引用 Vue代码优化 异步加载 所谓的异 ...
- 基于Tomcat的GeoServer部署步骤
一.安装JAVA 资源:JDK1.8 提取码:0y26 步骤: 1.安装完成后,右击"我的电脑",点击"属性",选择"高级系统设置": 2. ...
- Gitlab + Jenkins 的 CI 实践
0x00 事件 为了开发人员更高效的更新应用而采取的 CI 方式实践. 0x01 过程记录 1.Jenkins 设置 安装插件 Gitlab Hook Plugin Build Authorizati ...
- Linux7 64安装 oracle 11g Error in invoking target 'agent nmhs' of makefile
在makefile中添加链接libnnz11库的参数修改$ORACLE_HOME/sysman/lib/ins_emagent.mk,将$(MK_EMAGENT_NMECTL)修改为:$(MK_EMA ...
- 【Dos】复制指定文件夹下所有文件到另外指定文件夹下
bat代码如下: @echo off @set /p fromFile=from: @set /p toFile=to: rem 找到所有文件 dir /b /s %fromFile%\ *.gz & ...