JavaScript ES6 函数式编程入门经典 pdf
内容简介
《JavaScriptES6函数式编程入门经典》使用JavaScriptES6带你学习函数式编程。你将学习柯里化、偏函数、高阶函数以及Monad等概念。目前,编程语言已经将焦点从对象转移到函数。JavaScript支持函数式编程,并允许开发者编写精心设计的代码。
作者简介
AntoAravinth 是来自VisualBIChennai研发中心的高级商业智能开发工程师。在过去的五年中,他曾使用Java、JavaScript语言以及ReactJs、Angular等框架开发Web应用。他对Web和Web标准有透彻的理解。他也是流行框架ReactJs、Selenium和Groovy的开源贡献者。AntoAravinth在业余时间喜欢打乒乓球。他很有幽默感!他也是ReactQuickly一书的技术开发编辑,此书在2017年由Manning出版社出版。
目录
第1章函数式编程简介1
1.1什么是函数式编程?为何它重要1
1.2引用透明性4
1.3命令式、声明式与抽象5
1.4函数式编程的好处7
1.5纯函数7
1.5.1纯函数产生可测试的代码7
1.5.2合理的代码9
1.6并发代码10
1.7可缓存11
1.8管道与组合12
1.9纯函数是数学函数13
1.10我们要构建什么15
1.11JavaScript是函数式编程语言吗15
1.12小结16
第2章JavaScript函数基础17
2.1ECMAScript历史18
2.2创建并执行函数19
2.2.1第一个函数19
2.2.2严格模式21
2.2.3return语句是可选的22
2.2.4多语句函数22
2.2.5函数参数24
2.2.6ES5函数在ES6中是有效的24
2.3设置项目24
2.3.1初始设置24
2.3.2用第一个函数式方法处理循环问题26
2.3.3export要点28
2.3.4import要点28
2.3.5使用babel-node运行代码29
2.3.6在npm中创建脚本30
2.3.7从git上运行源代码31
2.4小结31
第3章高阶函数33
3.1理解数据34
3.1.1理解JavaScript数据类型34
3.1.2存储函数35
3.1.3传递函数35
3.1.4返回函数37
3.2抽象和高阶函数38
3.2.1抽象的定义38
3.2.2通过高阶函数实现抽象39
3.3真实的高阶函数42
3.3.1every函数42
3.3.2some函数44
3.3.3sort函数44
3.4小结48
第4章闭包与高阶函数49
4.1理解闭包50
4.1.1什么是闭包50
4.1.2记住闭包生成的位置52
4.1.3回顾sortBy函数53
4.2真实的高阶函数(续)54
4.2.1tap函数54
4.2.2unary函数56
4.2.3once函数57
4.2.4memoized函数58
4.3小结60
第5章数组的函数式编程61
5.1数组的函数式方法62
5.1.1map62
5.1.2filter65
5.2连接操作67
5.3reduce函数71
5.4zip数组77
5.5小结81
第6章柯里化与偏应用83
6.1一些术语84
6.1.1一元函数84
6.1.2二元函数84
6.1.3变参函数84
6.2柯里化86
6.2.1柯里化用例87
6.2.2日志函数——应用柯里化89
6.2.3回顾curry90
6.2.4回顾日志函数93
6.3柯里化实战94
6.3.1在数组内容中查找数字94
6.3.2求数组的平方95
6.4数据流96
6.4.1偏应用96
6.4.2实现偏函数97
6.4.3柯里化与偏应用99
6.5小结100
第7章组合与管道101
7.1组合的概念102
7.2函数式组合104
7.2.1回顾map与filter104
7.2.2compose函数106
7.3应用compose函数106
7.3.1引入curry与partial108
7.3.2组合多个函数111
7.4管道/序列113
7.5组合的优势114
7.5.1组合满足结合律114
7.5.2使用tap函数调试115
7.6小结116
第8章函子117
8.1什么是函子118
8.1.1函子是容器118
8.1.2函子实现了map方法120
8.2MayBe函子121
8.2.1实现MayBe函子122
8.2.2简单用例123
8.2.3真实用例125
8.3Either函子129
8.3.1实现Either函子130
8.3.2reddit例子的Either版本131
8.4Pointed函子134
8.5小结134
第9章深入理解Monad135
9.1根据搜索词条获取Reddit评论136
9.2问题描述136
9.2.1实现第一步138
9.2.2合并Reddit调用141
9.2.3多个map的问题144
9.3通过join解决问题146
9.3.1实现join146
9.3.2实现chain148
9.4小结151
第10章使用Generator153
10.1异步代码及其问题154
10.2Generator基础156
10.2.1创建Generator156
10.2.2Generator的注意事项157
10.2.3yield关键字158
10.2.4done属性160
10.2.5向Generator传递数据162
10.3使用Generator处理异步调用164
10.3.1一个简单的案例164
10.3.2一个真实的案例169
10.4小结172
附录173
感悟与笔记
第一章 函数式编程简介
1.1 什么是函数式编程?
f(x) = Y
函数式编程技术主要基于数学函数和它的思想。
看个栗子:
var percentValue = 5;
var caculateTax = (value) => {
return value/100 * (100 + percentValue)
}
这个caculateTax依赖于全局变量percentValue, 因此在数学意义上不能被称作真正的函数。修改成:
var caculteTax = (value, percentValue) => {
return value/100 * (100 + percentValue)
}
现在caculateTax算得上一个真正的函数了,通过这个简单的栗子,可以简单定义一下函数式编程:函数式编程是一种范式,我们能够一次创建仅依赖输入就可以完成自身逻辑的函数。这保证了当函数被多次调用时仍然返回相同的结果,不会改变任何外部环境的变量,这将产生可缓存、可测试的代码库。
函数与javascript方法
函数是一段可以通过其名称被调用的代码。它可以传递参数并返回值。
var simple = (a) => { return a } // 一个简单的函数
simple(5) // 用其名称调用
然而方法是一段必须通过其名称及其关联对象的名称被调用的代码。
var obj = { simple : (a) =>{ return a } }
obj.simple(5) // 用其名称及其关联对象调用
1.2 引用透明性
所有的函数对于相同的输入都将返回相同的值,这一特性被称为引用透明性。
一个简单的函数
var identity = (i) => { return i }
该函数满足引用透明性,现在它被用于其它函数调用之间。
sum(4, 5) + identity(1)
根据引用透明性,上面的语句可以转换成
sum(4, 5) + 1
这个过程被称为替换模型, 因为可以直接替换函数的结果(主要是因为函数的逻辑不依赖其它全局变量),这与它的值是一样的。这也使得并发代码和缓存成为可能。
遵循引用透明性的函数只依赖来自参数的输入, 不依赖全局数据。因此线程可以自由地运行,没有任何锁机制。
引用透明性是一种哲学
“引用透明性”一词来自分析哲学。该哲学分支研究自然语言的语义及其含义。单词“Referential”或“Referent”意指表达式引用的事物。句子中的上下文是“引用透明的”,如果用另一个引用相同实体的词语替换上下文中的一个词语,并不会改变句子的含义。
替换函数的值并不影响上下文,这就是函数式编程的哲学!
1.3 命令式、声明式与抽象
函数式编程主张声明式编程和编写抽象的代码。
来看个栗子:
// 打印数组的每个元素
var array = [1, 2, 3]
for (i = 0; i < array.length; i++)
console.log(array[i]) // 打印 1, 2, 3
用数组长度的索引计算结果编写了一个隐式的for循环并打印出数组项,"打印数组的元素",但是看起来像是在告诉编译器该做什么,在上面栗子中,告诉编译器“要获得数组长度、循环数组、用索引获取每一个数组元素,等等”我们将之称为“命令式”解决方案。命令式编程主张告诉编译器“如何”做。
现在来看另外一方面,声明式编程。在声明式编程中,我们要告诉编译器做“什么”,而不是“如何”做。“如何”做的部分将被抽象到普通函数中(这些函数被称为高阶函数),更改如下:
var array = [1, 2, 3]
array.forEach((element) => {
console.log(element) // 打印 1, 2, 3
})
这个代码片段移除了如何做的部分,只关心做什么的部分。
函数式编程主张以抽象的方式创建函数,这些函数能够在代码的其他部分被重用。
1.4 纯函数
大多数函数式编程的好处来自于编写纯函数,那么什么是纯函数?
纯函数:对给定的输入返回相同的输出的函数。
var double = (value) => value * 2
这个"double"函数就是一个纯函数,遵循引用透明性。
纯函数产生可测试的代码
不纯的函数具有副作用,例如之前的这个函数
var percentValue = 5;
var caculateTax = (value) => {
return value/100 * (100 + percentValue)
}
caculateTax不是纯函数,因为它依赖外部环境计算其逻辑。假设在运行相同的测试用例时,外部环境也在改变变量percentValue的值,那么之前给的定输入对应的就不是唯一的值了。
纯函数有一个重要属性:纯函数不应改变任何外部环境的变量。换而言之,纯函数不应依赖任何外部变量也不应改变任何外部变量。
// 举个反例
bar global = 'globalValue'
var badFunction = (value) => {
global = 'changed'
return value * 2
}
看看这个反例,当badFunction被调用时,它将全局变量global的值变更了,假设另一个函数的逻辑依赖global变量的话,调用badFunction就影响了其他函数的行为。具有这种性质的函数会使得代码库变得难以测试。
合理的代码
通过创建和使用纯函数,能够让我们非常简单的推理代码或函数。函数(无论它是否为纯函数)必须总是具有一个有意义的名称。例如double加倍函数就不能命名成dd。
用一个内置的Math.max函数来测试一下推理能力。
给定函数调用:
Math.max(3, 4, 5, 6)
为了给出结果,是不是没有看max的实现?为什么?因为Math.max是纯函数。
1.5 并发代码
纯函数总是允许我们并发地执行代码,因为纯函数不会改变它的环境。当然,js并没有真正的多线程来并发地执行函数,但如果你的项目使用了webworker来模拟多线程并行执行任务,这种时候就需要用纯函数来代替非纯函数。
举个栗子:
let global = "something"
let function1 = (input) => {
// 处理input
// 改变global
global = "somethingElse"
}
let function2 = () => {
if (global === "something") {
// 业务逻辑
}
}
如果我们需要并发的执行function1 和 function2,假设线程T-1选择function1执行,线程T-2选择function2执行,如果T-1在T-2之前执行,那么并发执行这些函数就会引起不良反应,现在把它们改为纯函数。
let function1 = (input, global) => {
// 处理input
// 改变global
global = "somethingElse"
}
let function2 = (global) => {
if (global === "somethins") {
// 业务逻辑
}
}
移动了global变量,把它作为两个函数的参数,使它们变成纯函数。由于函数并不依赖外部环境(global 变量),因此不必像之前那样担心线程的执行顺序。
1.6 可缓存
纯函数总是为给定的输入返回相同的输出,所以缓存纯函数的输出也是可能的。
举个栗子,有这么一个做耗时计算的函数。
var longRunningFunction = (ip) => {
// do long running tasks and return
}
然后我们有一个记账对象,它存储了longRunningFunction函数的所有调用结果。
var longRunningFnBookKeeper = {
2: 3,
4: 5,
...
}
使用纯函数的定义,在调用longRunningFunction之前检查key是否存在longRunningFnBookKeeper中,比如说:
// 检查key是否在longRunningFnBookKeeper中
// 如果在,则返回结果,否则更新记账对象
longRunningFnBookKeeper.hasOwnProperty(ip) ? longRunningFnBookKeeper[ip] : longRunningFnBookKeeper[ip] = longRunningFunction(ip)
这样就是缓存了纯函数的调用结果。
1.7 管道与组合
使用纯函数,我们只需要在函数中做一件事。纯函数应该被设计为只做一件事。只做一件事并把它做到完美是UNIX的哲学,我们在实现纯函数时也将遵循这一原则。UNIX和LINUX平台有很多用户日常任务的命令,例如:cat用于打印文件内容,grep用于搜索文件,wc用于计算行数。这些命令的确一次只解决一个问题,但是可以通过组合或者管道来完成复杂的任务。假如我们要在一个文件中找到一个特定的名称并统计它出现次数,在命令提示符中命令如下:
cat jsBook | grep -i "composing" | wc
组合不是UNIX/LINUX命令行独有的,但它们是函数式编程范式的核心。称为函数式组合。
javascript不支持组合函数函数的操作符"|",但是我们可以创建自己的支持组合的函数。
1.8 纯函数是数学函数
先见1.6 “可缓存”中的那段longRunningFunction和longRunningFnBookKeeper代码。
假设通过多次调用,longRunningFnBookKeeper增长为如下的对象:
longRunningFnBookKeeper = {
1: 32,
2: 4,
3: 5,
5: 6,
11: 44
}
分析一下该对象,可见longRunningFnBookKeeper接受一个输入并为给定的范围映射输出,此处的关键是,输入具有强制的、相应的输出。在key中也不存在映射两个输出的输入。
比对数学函数的定义(维基百科看不了),基本上与纯函数一致,从上述例子也能看到数学函数的思想被借鉴到函数式范式的世界。
1.9 javascript是函数式编程语言吗
javascript是函数式编程语言,函数式编程主张函数必须接受至少一个参数并返回一个值。坦率的说,可以创建一个不接收参数并且实际上什么也不返回的函数,例如这个:var useless = () => {},这串代码执行并不会报错,原因是javascript不是一种纯函数语言(比如说Haskell),而更像是一种多范式语言。
javascript支持将函数作为参数,以及将函数传递给另一函数等特性——主要原因是javascript将函数视为一等公民,由于函数定义的约束,开发者需要在创建javascript函数时将其考虑在内。
会员免费下载
链接:https://pan.baidu.com/s/1A4PIhYzphmawxZa733tVTg
提取码: ****** 查看
成为本站VIP会员即可无限下载。 请先点击百度网盘,看资源是否还在,不在请点击链接通知站长补资源。
资源标签点击标签可查看对应分类的资源