什么是函数式编程

简单说,"函数式编程"是一种 "编程范式"(programming paradigm),也就是如何编写程序的方法论。

它属于 "结构化编程" 的一种,主要思想是把运算过程尽量写成一系列嵌套的函数调用。举例来说,现在有这样一个数学表达式:

(5+6) - 1 * 3

传统的过程式编程,可能这样写:

var a = 5 + 6;
var b = 1 * 3;
var c = a - b;

函数式编程要求使用函数,我们可以把运算定义成不同的函数:

const add = (a, b) => a + b;
const mul = (a, b) => a * b;
const sub = (a,b) => a - b;sub(add(5,6), mul(1,3));

我们把每个运算包成一个个不同的函数,并且根据这些函数组合出我们要的结果,这就是最简单的函数式编程。

函数式编程基础条件

函数为一等公民 (First Class)

所谓 "一等公民"(first class),指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为其它函数的返回值。

函数赋值给变量:

const greet = function(msg) { console.log(`Hello ${msg}`); }
greet('Semlinker'); // Output: 'Hello Semlinker'

函数作为参数:

const logger = function(msg) { console.log(`Hello ${msg}`); };
const greet = function(msg, print) { print(msg); };
greet('Semlinker', logger);

函数作为返回值:

const a = function(a) {return function(b) {return a + b;};
};
const add5 = a(5);
add5(10); // Output: 15

函数式编程重要特性

只用表达式,不用语句

"表达式"(expression)是一个单纯的运算过程,总是有返回值;"语句"(statement)是执行某种操作,没有返回值。函数式编程要求,只使用表达式,不使用语句。也就是说,每一步都是单纯的运算,而且都有返回值。

原因是函数式编程的开发动机,一开始就是为了处理运算(computation),不考虑系统的读写(I/O)。"语句"属于对系统的读写操作,所以就被排斥在外。

Pure Function

Pure Function (纯函数) 的特点:

  • 给定相同的输入参数,总是返回相同的结果

  • 没有产生任何副作用

  • 没有依赖外部变量的值

所谓 "副作用")(side effect),是指函数内做了与本身运算无关的事,比如修改某个全局变量的值,或发送 HTTP 请求,甚至函数体内执行 console.log 都算是副作用。函数式编程强调函数不能有副作用,也就是函数要保持纯粹,只执行相关运算并返回值,没有其他额外的行为。

前端中常见的产生副作用的场景:

  • 发送 HTTP 请求

  • 函数内调用 logger 函数,如 console.log、console.dir 等

  • 修改外部变量的值

  • 函数内执行 DOM 操作

接下来我们看一下纯函数与非纯函数的具体示例:

纯函数示例:

const double = (number) => number * 2;
double(5);

非纯函数示例:

Math.random(); // => 0.3384159509502669
Math.random(); // => 0.9498302571942787
Math.random(); // => 0.9860841663478281

不修改状态 - 利用参数保存状态

函数式编程只是返回新的值,不修改系统变量。因此,不修改变量,也是它的一个重要特点。

在其他类型的语言中,变量往往用来保存"状态"(state)。不修改变量,意味着状态不能保存在变量中。函数式编程使用参数保存状态,最好的例子就是递归,具体示例如下:

function findIndex(arr, predicate, start = 0) {if (0 <= start && start < arr.length) {if (predicate(arr[start])) {return start;}return findIndex(arr, predicate, start+1);}
}
findIndex(['a', 'b'], x => x === 'b'); // 查找数组中'b'的索引值

示例中的 findIndex 函数用于查找数组中某个元素的索引值,我们通过 start 参数来保存当前的索引值,这就是利用参数保存状态。

引用透明

引用透明(Referential transparency),指的是函数的运行不依赖于外部变量或 "状态",只依赖于输入的参数,任何时候只要参数相同,引用函数所得到的返回值总是相同的。

非引用透明的示例:

const FIVE = 5;
const addFive = (num) => num + FIVE;
addFive(10);

函数式编程的优势

1.代码简洁,开发快速

函数式编程大量使用函数,减少了代码的重复,因此程序比较短,开发速度较快。

2.接近自然语言,易于理解,可读性高

函数式编程的自由度很高,可以写出很接近自然语言的代码。我们可以通过一系列的函数,封装数据的处理过程,代码会变得非常简洁且可读性高,具体参考以下示例:

[1,2,3,4,5].map(x => x * 2).filter(x => x > 5).reduce((p,n) => p + n);          

3.可维护性高、方便代码管理

函数式编程不依赖、也不会改变外界的状态,只要给定输入参数,返回的结果必定相同。因此,每一个函数都可以被看做独立单元,很有利于进行单元测试(unit testing)和除错(debugging),以及模块化组合。

4.易于"并发编程"

函数式编程不需要考虑"死锁"(deadlock),因为它不修改变量,所以根本不存在"锁"线程的问题。不必担心一个线程的数据,被另一个线程修改,所以可以很放心地把工作分摊到多个线程,部署"并发编程"(concurrency)。

函数式编程中常用方法

forEach

在 ES 5 版本之前,我们只能通过 for 循环遍历数组:

var heroes = ['Windstorm', 'Bombasto', 'Magneta', 'Tornado'];
for (var i =0, len = heroes.length; i < len; i++) {console.log(heroes[i]);
}

在 ES 5 版本之后,我们可以使用 forEach 方法,实现上面的功能:

forEach 方法签名:

array.forEach(callback[, thisArg])

参数说明:

  • callback - 对数组中每一项,进行处理的函数

    • currentValue - 数组中正在处理的当前元素

    • index - 数组中正在处理的当前元素的索引

    • array - 处理的数组

  • thisArg (可选的) - 设置执行 callback 函数时,this 的值

以上示例 forEach 方法实现:

var heroes = ['Windstorm', 'Bombasto', 'Magneta', 'Tornado'];
heroes.forEach(name => console.log(name));

map

在 ES 5 版本之前,对于上面的示例,如果我们想给每个英雄的名字添加一个前缀,但不改变原来的数组,我们可以这样实现:

var heroes = ['Windstorm', 'Bombasto', 'Magneta', 'Tornado'];
var prefixedHeroes = [];
for (var i =0, len = heroes.length; i < len; i++) {prefixedHeroes.push('Super_' + heroes[i]);
}

在 ES 5 版本之后,我们可以使用 map 方法,方便地实现上面的功能。

map 方法签名:

const new_array = arr.map(callback[, thisArg])

参数说明:

  • callback - 对数组中每一项,进行映射处理的函数

    • currentValue - 数组中正在处理的当前元素

    • index - 数组中正在处理的当前元素的索引

    • array - 处理的数组

  • thisArg (可选的) - 设置执行 callback 函数时,this 的值

以上示例 map 方法实现:

var heroes = ['Windstorm', 'Bombasto', 'Magneta', 'Tornado'];
var prefixedHeroes = heroes.map(name => 'Super_' + name);

filter

在 ES 5 版本之前,对于 heroes 数组,我们想获取名字中包含 m 字母的英雄,我们可以这样实现:

var heroes = ['Windstorm', 'Bombasto', 'Magneta', 'Tornado'];
var filterHeroes = [];
for (var i =0, len = heroes.length; i < len; i++) {if(/m/i.test(heroes[i])) {filterHeroes.push(heroes[i]);}
}

在 ES 5 版本之后,我们可以使用 filter 方法,方便地实现上面的功能。

filter 方法签名:

var new_array = arr.filter(callback[, thisArg])

参数说明:

  • callback - 用来测试数组的每个元素的函数。调用时使用参数 (element, index, array)。返回true表示保留该元素(通过测试),false则不保留。

  • thisArg (可选的) - 设置执行 callback 函数时,this 的值

以上示例 filter 方法实现:

var heroes = ['Windstorm', 'Bombasto', 'Magneta', 'Tornado'];
var filterRe = /m/i;
var filterHeroes = heroes.filter(name => filterRe.test(name));

参考资源

  • 函数式编程初探

  • 30天精通RxJS(02) 函数式编程基本概念

RxJS Functional Programming相关推荐

  1. RxJS 系列之一 - Functional Programming 简介

    RxJS 系列目录 RxJS 系列之一 - Functional Programming 简介 (本文) RxJS 系列之二 - Observable 详解 RxJS 系列之三 - Operators ...

  2. 30 天精通 RxJS (03): Functional Programming 通用函数

    了解 Functional Programming 的通用函数,能让我们写出更简洁的代码,也能帮助我们学习 RxJS. 这是[30天精通 RxJS]的 03 篇,如果还没看过 02 篇可以往这边走: ...

  3. Clojure入门教程: Clojure – Functional Programming for the JVM中文版

    http://xumingming.sinaapp.com/302/clojure-functional-programming-for-the-jvm-clojure-tutorial/ api:h ...

  4. Functional Programming 资料收集

    书籍: Functional Programming for Java Developers SICP(Structure and Interpretation of Computer Program ...

  5. 啥叫“Functional Programming ”???

    啥叫"Functional Programming "??? 我们华师大软件工程专业开始上课了,用的全是英文版教材. 我们有一门课叫做 Functional Programming ...

  6. Clojure – Functional Programming for the JVM中文版

    Clojure – Functional Programming for the JVM中文版 发表于 2011 年 12 月 07 日 由xumingming 作者: xumingming | 可以 ...

  7. [翻译]Why Functional Programming Matters

    Why Functional Programming Matters 函数式程序设计为什么至关重要 作者: John Hughes 翻译: CloudiDust [http://blog.csdn.n ...

  8. 重温“卡马克谈functional programming in c++”

    正在准备些关于overwatch的gameplay architecture的blog,里面的观点也让我想起12年时候看的carmack谈的functional programming in c++这 ...

  9. Kotlin 函数式编程(Kotlin Functional Programming)

    Kotlin 函数式编程 (Kotlin Functional Programming)     陈光剑 1. 函数式概述 6 1.1. 函数式简史 6 1.2. 函数式编程语言家族 7 1.2.1. ...

最新文章

  1. python 爬虫实例 电影-Python爬虫入门实战之猫眼电影数据抓取(实战篇)
  2. border和图片之间有缝隙_院子里现浇水泥板,每块之间留7公分的缝,铺出来的效果漂亮大气...
  3. 中班机器人教室设计方案_奇思妙想一起玩,机器人来了安格利亚东郡生态幼儿园亲子活动...
  4. Html文件转换为Aspx文件后发现样式丢失或失效
  5. RabbitMQ学习之Flow Control
  6. 万能模拟器eve-ng介绍
  7. Atitit 视图状态ViewState)的原理与管理
  8. 【Unity游戏开发】不接SDK也能在游戏内拉起加QQ群操作?
  9. 经典的机器人入门资料
  10. 64位处理器_为什么苹果A处理器领先安卓2年?从什么时候开始领先的?有何依据...
  11. 计算机硬盘内存显示是红色,解决 win10 磁盘容量条显示红色问题
  12. 如何理解keras中的shape/input_shape
  13. UITableViewCell设置行距
  14. 100G QSFP28 CLR4单模光模块的介绍及对比
  15. 优雅华美 简约奢华 轻奢风
  16. python爬取推特的详细教程_使用Selenium Python进行网页抓取[Twitter+Instagram]
  17. ISO26262第二版发布
  18. 机械制造工艺及计算机辅助工艺设计,《机械制造工艺及计算机辅助工艺设计》曾淑畅著【摘要 书评 在线阅读】-苏宁易购图书...
  19. 关于正确处理0x80070426等错误的方法
  20. web实验报告——JSP动态网页编程

热门文章

  1. c创建python虚拟机_Docker-ce运用一:创建虚拟机
  2. linux时钟告警,Linux记录-告警脚本
  3. 笔记本vm系统的分辨率不好调整_关于超高分辨率小动物超声成像系统(3100LT)和多模式、超高分辨率小动物光声/超声成像系统(2100)测试费价格调整通知...
  4. mysqlplus 批量插入_ibatis结合oracle批量插入三种方法的测评
  5. easyexcel注解样式无效_【Java神器】用@ResponseExcel注解将Java List直接转换为Excel文件。不好意思,今晚又可以早点下班了!...
  6. free网页服务器,Web网站服务(一)
  7. python压缩映射过滤器
  8. linux定时调用程序参数说明
  9. 关于CSS3圆角矩形的一些学习探讨
  10. symantec 操作 重叠vo_无关收购 谈谈赛门铁克的产品策略思路