第859期成为一名函数式码农之二
Posted 前端早读课
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第859期成为一名函数式码农之二相关的知识,希望对你有一定的参考价值。
前言
2月份最后一周了,今年过了六分之一了。2月27号早读文章同样来自@唐先僧翻译授权分享。
正文从这开始~
请仔细阅读文中的代码。确保你已经理解了代码之后再进行下一步。每一节都是建立在前一节的基础上。
如果你匆忙行事,你可能会错过一些对后面章节很重要的细微差别。
重构
我们先来看一下重构问题。这里有一些javascript代码片段:
我们先前都这样写代码,一段时间后我们开始意识到这两个函数实际上功能是一样的只有一小部分不一样(已经以粗体显示)。
我们不应该复制粘贴validateSsn然后修改一些代码来创建validatePhone,而应该创建一个单独的函数通过参数来处理粘贴以后修改的部分。
在本例中我们应该通过参数处理value,regular expression和打印的message(至少打印消息的最后一部分)。
重构后的代码:
value代表之前代码中的ssn和phone。
regex代表之前的正则表达式/^d{3}-d{2}-d{4}$/ 和 /^(d{3})d{3}-d{4}$/。
最后,消息的最后一部分即‘SSN’和‘Phone Number’由type表示。
使用一个函数比使用两个函数或者更糟的3个、4个或者10个函数要好一些。这使得代码整洁并且可维护。
例如,如果发现一个bug,你只需要修改一个地方即可,而不是搜索整个代码库来查找可能粘贴并修改这个函数的地方。
但是如果有以下这样的情况会怎样:
在这parseAddress和parseFullName函数都是接受一个string,如果传入的字符串符合预订的句法则返回true。
我们怎么来重构这段代码?
好的,我们可以像前面一样使用value来代表address和name,使用type来代表‘Address’和‘Name’,但是我们的正则表达式之前是一个函数。
如果我们可以将一个函数作为参数传递。。。
高阶函数
许多语言不支持将函数作为参数传递。有的支持但是变得非常复杂。
在函数式语言中,函数是这个语言中的一等公民。换句话说,函数仅仅是另一种值而已。
由于函数仅仅是值,我们可以将它们作为参数传递。
尽管JavaScript不是纯粹的函数式语言,你可以用它做一些函数式操作。所以这里我们将前面两个函数重构成一个函数,将parsing function(正则表达式匹配函数)通过参数parseFunc传递进去:
我们的新函数就称为高阶函数(Higher-order Function)。
高阶函数可以接收函数作为参数,或者返回一个函数结果,或者两者同时具备。
现在可以使用我们的高阶函数来替代前的4个函数(这个在JavaScript中可以工作,因为Regex.exec匹配成功会返回true):
这比前面使用4个几乎一模一样的函数要好多了。
但是请注意正则表达式。它们有一点啰嗦。我们再重构一下:
但是想象一下我们有更多的正则表达式要分析,不仅仅是parseSsn和parsePhone。每次我们创建一个正则表达式解析器,我们需要记住添加.exec到它的末尾。说真的,对我来说很容易忘记。
我们可以通过创建一个返回exec函数的高阶函数来防止这种情况:
这里makeRegexParser接收一个正则表达式并返回exec函数,exec函数接收一个字符串参数。validateValueWithFunc函数会传递字符串(value)给解析函数,即exec。
的确,这是一个微不足道的改进,但是这里展示了一个高阶函数返回一个函数的例子。
但是,你可以想象一下如果makeRegexParser比现在要复杂得多的时候,这个改进所带来的好处。
这里还有另外一个高阶函数返回函数的例子:
makeAdder接收一个参数constantValue返回一个adder,adder是一个函数,它将给它接收的参数加上一个常量值。
这是它怎么被使用:
我们通过向makeAdder函数(它会返回一个函数)传递一个常量10来创建一个函数add10,add10将给任意值加上10。
注意adder函数可以访问constantValue,即使makeAddr函数已经返回。因为adder在创建时constantValue在它的作用域内。
这个行为非常的重要,因为如果没有它,能够返回函数的函数不是很有用。所以我们理解它是怎么工作以及怎么称呼它非常要。
这种行为成为闭包。
闭包(Closures)
这里有一个人为的使用闭包的函数例子:
在这个例子中,child可以访问它自己的变量,parent的变量以及grandParent的变量。
parent可以访问它自己的变量和grandParent的变量。
grandParent只能访问它自己的变量。
(可以参考上面的金字塔来理解)
这里是它的使用范例:
这里,parentFunc保持parent的作用域存活,因为grandParent返回的是parent函数。
类似的,childFunc保持child的作用域存活,因为parentFunc(这里就是parent)返回child函数。
当一个函数被创建,其整个生命周期中都是可以访问在在其创建时作用域内的所有变量。只要有引用指向它该函数就会一直存在。例如:只要childFunc还引用它,child的作用域就一直存在。
闭包就是一个函数的作用域,这个作用域通过指向该函数的引用保持存活。
注意在JavaScript中闭包是会造成问题的,因为这些变量是可修改的,即,从它们结束一直到他们返回的函数被调用期间,它们都可以修改变量。
幸亏,函数式语言中的变量都是不可修改的,这样可以消除这个常见的bug以及困惑根源。
关于本文
译者:@唐先僧
译文:http://www.jianshu.com/p/7cfb1ffa1db5
原文:https://medium.com/@cscalfani/so-you-want-to-be-a-functional-programmer-part-2-7005682cec4a#.s1r0k68u4
以上是关于第859期成为一名函数式码农之二的主要内容,如果未能解决你的问题,请参考以下文章