。 当您成为更好的程序员时,您会编写较小的功能,更好地重复使用代码,为您的代码编写测试,并相信您编写的程序将继续按照您的意图进行。 没有人喜欢在代码中查找和修复错误,因此成为更好的程序员也是要避免某些容易出错的事情。 通过经验或听取经验丰富的人的建议来避免什么,例如道格拉斯·克罗克福德(Douglas Crockford)在JavaScript中著名地解释了:
。功能编程为我们提供了通过将程序简化为最简单的形式来降低程序复杂性的方法:表现得像纯数学功能的功能。 学习功能编程的原理是您技能集的一个很好的补充,它将帮助您使用更少的错误编写更简单的程序。
功能编程的关键概念是纯粹的函数,不可变的值,组成和驯服副作用。
纯函数是一个函数,给定相同的输入将始终返回相同的输出,并且没有任何可观察到的副作用。
<span>// pure </span><span>function add(a<span>, b</span>) { </span> <span>return a + b; </span><span>} </span>此功能是
纯。它不依赖或更改功能之外的任何状态,它将始终>始终返回相同输入的相同输出值。
此函数是<span>// impure </span><span>var minimum = 21; </span><span>var checkAge = function(age) { </span> <span>return age >= minimum; // if minimum is changed we're cactus </span><span>}; </span>不纯
,因为它依赖于功能之外的外部可突变状态。 如果我们将此变量移入功能的内部,它将变得纯净,我们可以确定我们的函数将正确检查我们的年龄
。纯函数没有侧面效应
。这里有一些重要的要记住:<span>// pure </span><span>var checkAge = function(age) { </span> <span>var minimum = 21; </span> <span>return age >= minimum; </span><span>}; </span>>
>访问函数之外的系统状态
突变对象作为参数纯函数的好处
<span>// impure, splice mutates the array </span><span>var firstThree = function(arr) { </span> <span>return arr.splice(0,3); // arr may never be the same again </span><span>}; </span> <span>// pure, slice returns a new array </span><span>var firstThree = function(arr) { </span> <span>return arr.slice(0,3); </span><span>}; </span>
纯粹的功能对其不纯净的对应物有一些好处:
<span>let items = ['a','b','c']; </span><span>let newItems = pure(items); </span><span>// I expect items to be ['a','b','c'] </span>更容易测试,因为它们的唯一责任是映射输入 - >输出
>自我记录,因为该函数的依赖项是明确的
>
>除非您具有功能编程背景这些抽象(咖喱,过度使用组合和道具),这确实是很难遵循的,而执行流也是如此。 下面的代码更容易理解和修改,它也比上面纯粹的功能方法更清楚地描述了程序。
<span>// pure </span><span>function add(a<span>, b</span>) { </span> <span>return a + b; </span><span>} </span>或,这种替代的API使用诸如提取和承诺之类的抽象来有助于我们进一步阐明我们的异步行动的含义。
>
<span>// impure </span><span>var minimum = 21; </span><span>var checkAge = function(age) { </span> <span>return age >= minimum; // if minimum is changed we're cactus </span><span>}; </span>注意:提取和承诺是即将到来的标准,因此它们需要今天使用多填充。
ajax请求和dom操作永远不会纯粹,但是我们可以从其余的纯粹的功能中发挥出纯粹的功能,将响应json映射到一系列图像中 - 让我们暂时求依赖对jQuery的依赖。
>我们的功能现在只是在做两件事:
<span>// pure </span><span>var checkAge = function(age) { </span> <span>var minimum = 21; </span> <span>return age >= minimum; </span><span>}; </span>
映射响应数据 - > URL
撰写返回一个函数列表组成的函数,每个函数都消耗了随后的函数的返回值。
<span>// impure, splice mutates the array </span><span>var firstThree = function(arr) { </span> <span>return arr.splice(0,3); // arr may never be the same again </span><span>}; </span> <span>// pure, slice returns a new array </span><span>var firstThree = function(arr) { </span> <span>return arr.slice(0,3); </span><span>}; </span>
>有助于阅读参数以从右到左构成以了解数据流的方向。这是组成的作品,将URL的响应传递到我们的图像功能中。
,这当然不是我们想要的。 询问重构代码时最重要的问题是:
<span>let items = ['a','b','c']; </span><span>let newItems = pure(items); </span><span>// I expect items to be ['a','b','c'] </span>>代码更易于阅读和理解?
>
基本函数>现在,我根本不是要攻击功能编程。每个开发人员都应进行一致的努力来学习基本功能,使您在编程中抽象共同的模式,以更简洁的声明代码,或者正如Marijn Haverbeke所说的那样。
>具有基本功能曲目的程序员,更重要的是,关于如何使用它们的知识比从头开始的人更有效。 - 雄辩的JavaScript,Marijn Haverbeke
这是每个JavaScript开发人员都应该学习和掌握的基本功能的列表。这也是一种掌握JavaScript技能的好方法,可以从头开始编写这些功能。
<span>// pure </span><span>function add(a<span>, b</span>) { </span> <span>return a + b; </span><span>} </span>降低功能依赖于共享状态
这听起来可能很明显,但我仍然编写访问和修改许多状态的功能,这使它们更难测试,并且更容易出错。
使用更可读的语言摘要,例如foreach to Iterate
<span>// impure </span><span>var minimum = 21; </span><span>var checkAge = function(age) { </span> <span>return age >= minimum; // if minimum is changed we're cactus </span><span>}; </span>
使用更高级别的抽象(例如地图)来减少代码
<span>// pure </span><span>var checkAge = function(age) { </span> <span>var minimum = 21; </span> <span>return age >= minimum; </span><span>}; </span>的代码量
将功能降低到其最简单的形式
<span>// impure, splice mutates the array </span><span>var firstThree = function(arr) { </span> <span>return arr.splice(0,3); // arr may never be the same again </span><span>}; </span> <span>// pure, slice returns a new array </span><span>var firstThree = function(arr) { </span> <span>return arr.slice(0,3); </span><span>}; </span>>
删除代码,直到停止工作
<span>let items = ['a','b','c']; </span><span>let newItems = pure(items); </span><span>// I expect items to be ['a','b','c'] </span>
>我们根本不需要一个功能来完成如此简单的任务,该语言为我们提供了足够的抽象来逐字写出。
测试
<span>import _ from 'ramda'; </span><span>import $ from 'jquery'; </span> <span>var Impure = { </span> <span>getJSON: _.curry(function(callback<span>, url</span>) { </span> $<span>.getJSON(url, callback); </span> <span>}), </span> <span>setHtml: _.curry(function(sel<span>, html</span>) { </span> <span>$(sel).html(html); </span> <span>}) </span><span>}; </span> <span>var img = function (url) { </span> <span>return $('<img />', { src: url }); </span><span>}; </span> <span>var url = function (t) { </span> <span>return 'http://api.flickr.com/services/feeds/photos_public.gne?tags=' + </span> t <span>+ '&format=json&jsoncallback=?'; </span><span>}; </span> <span>var mediaUrl = _.compose(_.prop('m'), _.prop('media')); </span><span>var mediaToImg = _.compose(img, mediaUrl); </span><span>var images = _.compose(_.map(mediaToImg), _.prop('items')); </span><span>var renderImages = _.compose(Impure.setHtml("body"), images); </span><span>var app = _.compose(Impure.getJSON(renderImages), url); </span><span>app("cats"); </span>能够简单地测试我们的程序是纯粹功能的关键好处,因此在本节中,我们将为我们较早查看的Flickr模块设置测试安全带。
>摩卡具有许多方便的功能,例如描述,它可以分解我们的测试和挂钩,例如以前和之后进行设置和拆除任务。 断言是一个可以执行简单平等测试,断言和断言的核心节点软件包。DeepEqual是最有用的功能。
>让我们在test/example.js<span>var app = (tags)=> { </span> <span>let url = <span>`http://api.flickr.com/services/feeds/photos_public.gne?tags=<span>${tags}</span>&format=json&jsoncallback=?`</span> </span> $<span>.getJSON(url, (data)=> { </span> <span>let urls = data.items.map((item)=> item.media.m) </span> <span>let images = urls.map((url)=> $('<img />', { src: url }) ) </span> <span>$(document.body).html(images) </span> <span>}) </span><span>} </span><span>app("cats") </span>中写下我们的第一个测试
>打开软件包。
然后,您应该能够从命令行运行NPM测试,以确认所有内容都是按预期工作的。
><span>let flickr = (tags)=> { </span> <span>let url = <span>`http://api.flickr.com/services/feeds/photos_public.gne?tags=<span>${tags}</span>&format=json&jsoncallback=?`</span> </span> <span>return fetch(url) </span> <span>.then((resp)=> resp.json()) </span> <span>.then((data)=> { </span> <span>let urls = data.items.map((item)=> item.media.m ) </span> <span>let images = urls.map((url)=> $('<img />', { src: url }) ) </span> <span>return images </span> <span>}) </span><span>} </span><span>flickr("cats").then((images)=> { </span> <span>$(document.body).html(images) </span><span>}) </span>
boom。
<span>let responseToImages = (resp)=> { </span> <span>let urls = resp.items.map((item)=> item.media.m ) </span> <span>let images = urls.map((url)=> $('<img />', { src: url })) </span> <span>return images </span><span>} </span>>注意:如果您希望Mocha观察更改并自动运行测试,则还可以在此命令的末尾添加一个-w标志,它们将在重新运行时运行得更快。
>
<span>let urls = (data)=> { </span> <span>return data.items.map((item)=> item.media.m) </span><span>} </span><span>let images = (urls)=> { </span> <span>return urls.map((url)=> $('<img />', { src: url })) </span><span>} </span><span>let responseToImages = _.compose(images, urls) </span>测试我们的flickr模块
让我们将模块添加到lib/flickr.js
<span>let responseToImages = (data)=> { </span> <span>return images(urls(data)) </span><span>} </span>我们的模块正在公开两种方法:flickr将被公开消费和私人功能_responSetoimages,以便我们可以孤立地测试。
<span>// pure </span><span>function add(a<span>, b</span>) { </span> <span>return a + b; </span><span>} </span>
>打开test/_setup.js,我们将使用模块依赖的Globals配置JSDOM。
<span>// impure </span><span>var minimum = 21; </span><span>var checkAge = function(age) { </span> <span>return age >= minimum; // if minimum is changed we're cactus </span><span>}; </span>>我们的测试可以坐在测试/flickr.js中,我们将对预定义输入的功能发出输出发出断言。 我们“存根”或覆盖全局获取方法以拦截和伪造HTTP请求,以便我们可以直接击中Flickr API进行测试。
>
<span>// pure </span><span>var checkAge = function(age) { </span> <span>var minimum = 21; </span> <span>return age >= minimum; </span><span>}; </span>>通过NPM测试再次运行我们的测试,您应该看到三个保证绿色tick。
ph!我们已经成功测试了我们的小模块以及构成它的功能,了解纯粹的功能以及如何在此过程中使用功能组合物。 我们已经将纯净的纯度分开,它是可读的,由小功能组成,并且经过了经过良好的测试。 代码比上面的不合理的纯
<span>// impure, splice mutates the array </span><span>var firstThree = function(arr) { </span> <span>return arr.splice(0,3); // arr may never be the same again </span><span>}; </span> <span>// pure, slice returns a new array </span><span>var firstThree = function(arr) { </span> <span>return arr.slice(0,3); </span><span>}; </span>示例更容易读取,理解和修改
,这是我在重构代码时的唯一目标。 纯函数,使用它们。> 链接
> Frisby教授的功能编程大多数指南 - @drboolean - Brian Lonsdorf的这本出色的有关功能编程的免费书籍是我遇到的FP的最佳指南。 本文中的许多想法和示例来自本书。
>纯函数在功能编程中的重要性是什么?它们是始终为相同输入产生相同输出的功能,并且没有副作用。这意味着他们不会改变范围之外的任何状态或依赖任何外部状态。这使它们可以预测且易于测试,因为您只需要考虑输入和输出而不必担心外部因素。纯函数还可以促进代码可重复性和可读性,使您的代码易于理解和维护。
>
>在JavaScript中使用纯函数的挑战是什么? ,他们还提出了一些挑战。主要挑战之一是JavaScript不是纯粹的功能性语言,它允许副作用和可变数据。这意味着您需要谨慎避免在功能中无意中引入副作用。此外,使用纯函数有时会导致更多的详细代码,因为您需要避免突变数据并返回新数据。功能组成是功能编程中的基本概念。它涉及组合两个或多个函数以创建一个新功能。一个函数的结果用作下一个函数的输入。这使您可以通过简单函数构建复杂的功能,促进代码可重复性和可读性。
不变性是功能编程的关键原理。这意味着一旦创建了数据结构,就无法更改。相反,如果要修改数据结构,则创建一个具有所需更改的新数据结构。这避免了副作用,并使您的代码更安全,更易于推理。
>在功能编程中,仔细处理状态以避免副作用。功能编程没有改变状态,而是使用返回新状态的纯函数。这使国家可预测且易于管理。一些功能性编程语言还为州管理提供了高级功能,例如Haskell中的Monad。 。在并发和并行性很重要的情况下,例如在多核和分布式计算中,它特别有用。功能编程也通常用于数据处理和分析中,纯粹的功能和不变性可以帮助确保数据完整性。此外,在前端开发中越来越多地采用了功能编程概念,诸如react.js之类的流行框架使用功能风格进行组件开发。
以上是合理纯粹的功能编程简介的详细内容。更多信息请关注PHP中文网其他相关文章!