8年老司机告诉你如何学JavaScript函数式编程

分享之前我还是要推荐下我自己的前端学习群:685910553,不管你是小白还是大牛,我都欢迎,不定期分享干货,包括我自己整理的一份2019最新的前端资料和零基础入门教程,送给大家,欢迎初学和进阶中的小伙伴

什么是函数式编程(Functional Programming)**

在开始之前,让我们先花一点时间来了解一下一些实用的函数式编程概念。

函数式编程把“function”作为重复使用的主要表达式。通过构建专注于某个特定任务的小函数,函数式编程使用合成(compose)来构建更复杂的函数 ——这就是 Currying(柯里化)和 Partial Application(偏函数应用)这样的技术发挥作用的地方了。

函数式编程使用函数作为重复使用的声明表达式,避免对状态进行修改,消除了副作用,并使用合成来构建函数。

功能编程本质上是用功能编程的!额外需要考虑的是:如避免状态改变,无副作用的纯函数,消除循环支持递归是纯函数式编程方法的一部分,用 Haskell语言是这样构建的。

我们将重点介绍函数式编程的实用部分,以便我们可以在本系列博客文章中立即使用 Javascript 。

高阶函数(Higher Order Functions) – JavaScript中函数是“一等公民(first-class)”,这意味着我们可以将函数作为参数传递给其他函数;也可以将函数作为其他函数的值返回。渔人码头注:以函数为参数或返回值的函数称为“高阶函数”。

装饰器(Decorators) –因为 JavaScript中函数可以是高阶函数,所以我们可以创建函数来增加其他函数的行为和/或作为其他函数的参数。

合成(Composition) –我们还可以创建由多个函数合成的函数,创建链式的输入处理。

我们将介绍我们要使用的技术,以便在需要时利用这些特性。这让我们可以在上下文环境中引入它们,并使概念易于消化和理解。

让我们开始吧

我们来看一个典型的例子,它需要处理从异步请求中获取的一些数据。在这种情况下,异步获取数据采用了JSON格式,并包含了一个博客文章的摘要列表。

以下是我们将使用的异步获取数据:查看 Gist中的完整数据和示例数据。

在这里插入图片描述
我们的需求:现在,假设我们想要显示最近的文章(不超过一个月),按标签分组,按发布日期排序。让我们思考一下我们需要做些什么

过滤掉一个月以前的文章(比如30天)。

通过他们的 tags对文章进行分组(这可能意味着如果他们有多个标签,则会显示在两个分组中。)

按发布日期排序每个标签列表,降序。

我们将在本系列文章中涵盖上述每个需求,这篇文章从过滤开始。
过滤数据

我们的第一步是过滤掉发布日期超过 30天的文章记录。由于函数式编程都是作为重用的主要表达式的函数,所以让我们构建一个函数来封装过滤列表的行为。

有些朋友可能会问,“真的吗?就这样好了吗?”

嗯,是的,没有更多要写的了。

这个函数使用 predicate断言函数(fn)来过滤一个数组(list),或许你会说,这可以通过直接调用 list.filter(fn)来轻松实现。那么为什么不这样做呢?

因为当我们将操作抽象成一个函数时,我们就可以使用 Currying(柯里化)来构建一个更有用的函数。

Currying(柯里化)是使用 N个参数的函数,返回一个 N个函数的嵌套系列,每个函数都采用 1个参数。

有关 Currying(柯里化)概念的更多信息,请阅读我以前的文章,并实现 left -> right的 currying(柯里化) 。
在这里插入图片描述
一步一步教你 JavaScript 函数式编程(第一部分)

在这种情况下,我们将使用一个名为 rightCurry的函数,该函数将函数的参数从右向左进行柯里化。通常,一个普通 curry函数会将参数从左到右进行柯里化。
这是我们的实现,以及它在内部使用的另一个实用函数 flip 。
在这里插入图片描述
一步一步教你 JavaScript 函数式编程(第一部分)

通过 currying(柯里化) ,我们可以创建一些函数,允许我们创建新的、偏应用的函数,我们可以重用这些函数。在我们这个例子中,我们将使用它来创建一个函数,该函数部分应用 predicate断言函数(fn)来进行过滤列表的操作。

这基本上与手动调用二元的 filter(list, fn)函数一样,进行相同的操作。
在这里插入图片描述
一步一步教你 JavaScript 函数式编程(第一部分)

我们可以如下使用它吗?
在这里插入图片描述
一步一步教你 JavaScript 函数式编程(第一部分)

哇,可以!最初似乎是很多的工作;但是我们从这个方法中得出的结论是:

使用 currying(柯里化)创建一个通用的,可重用的函数,filterWith ,可以在许多情况下使用它来创建更具体的列表过滤器

每当我们得到一些数据时,都可以懒惰地执行这个新的过滤器。我们不能做到调用 Array.prototype.filter的同时,不使其立即对数据列表执行操作

一个更具声明性的API,有助于可读性和理解
关于 predicate断言函数

我们的 filterWith函数需要一个 predicate断言函数,当给定列表中的某个元素时,它返回true或false,以确定是否应该在新过滤的列表中返回该元素。

让我们从一个更通用的比较函数开始,它可以告诉我们一个给定的数是否大于或等于另一个数。

我们文章的发布日期可以转换成数字,时间戳格式(自Epoch以来的毫秒数)这应该可以正常工作。但是,用于过滤数组的断言函数只能传递一个参数来检查,而不是两个。

那么,在需要一元函数的情况下,如何使我们的二元比较函数工作呢?

Currying(柯里化)可以再次拯救我们!我们将使用它来创建一个函数,该函数可以创建一元比较函数。

var greaterThanOrEqualTo = rightCurry(greaterThanOrEqual);

我们现在可以使用这个柯化版本来创建一个 predicate断言函数,可以用于列表过滤,例如:

在这里插入图片描述
棒极了!现在我们回到我们的示例,创建一个 predicate断言函数,具体解决我们原先的过滤掉发布在30天以前的文章了:

到现在为止还挺好!

我们创建了一个可以轻松重用的过滤断言函数。另外,因为我们使用的是函数式方法,所以我们的代码更具声明性,易于遵循 –它的读取方式与工作原理完全相同。可读性和维护是编写任何代码时需要考虑的重要事情!

类型问题…

呃,我们还有另一个问题!我们的程序需要过滤的是一个对象列表,所以我们的 predicate断言函数将需要访问传入的每一项的 published属性。

我们目前的 predicate断言函数,within30Days不能处理对象类型的参数,只能处理具体的数值!让我们用另一个函数来解决这个问题吧!(你在这里看到一个模式了吗?)

我们想重用我们现有的断言函数;但修改其参数,以便它可以与我们的特定对象类型一起使用。这是一个新的实用函数,让我们通过修改其参数来扩展现有的函数。
在这里插入图片描述
这是迄今为止最有趣的函数式实用工具函数,并且几乎与 Ramda.js库中相同名称的函数相同。

useWith返回一个修改原来函数(fn)的函数,所以当被调用时,它将通过相应的变换(txnfn)函数传递每个参数。如果在调用时比转换函数有更多的参数,那么剩下的参数将会以 “as is”的形式传递。

让我们用一个小例子来帮助解释这个定义。简单地说,useWith让我们执行以下操作:
在这里插入图片描述
当我们调用 additiveSum(4,5)时,我们基本上可以得到以下调用栈:

additiveSum(4,5)

add1(4) => 5

add1(5) => 6

sum(5, 6) => 11

我们可以使用 useWith来修改现有的 predicate断言函数来在对象类型上操作,而不是数值。首先,让我们再次使用 currying(柯里化)来创建一个函数,该函数允许我们创建偏应用的函数,这些函数可以通过属性名访问对象。

现在我们可以使用 getWith作为变换函数,从每个对象获取 .published日期,传递给用于过滤器(filter)的一元断言函数。
在这里插入图片描述
我们来试试看一下测试数据:
在这里插入图片描述
准备过滤!

好的,鉴于我们的第一个需求是保留最近30天内的文章记录,那么用我们的响应数据来提供一个完整的实现。
在这里插入图片描述
最后在对各位刚刚入门前端的程序员提点建议。

对于刚刚参加工作的同学来说,思考比做事更重要。如果你为了业务而业务,不停的去堆积,只能说过些年你还是如此。去好好的想一想,编程到底是在做什么?

如果你现在还准备去学习前端 ,那至少你可以通过社区来了解前端的发展动态,去了解出现了哪些新的框架,更新了哪些新的Api或者属性。未来一段时间内,国内或者国际厂商会使用哪些技术等等。

在此我还是要推荐下我自己的前端学习群:685910553,不管你是小白还是大牛,小编我都挺欢迎,不定期分享干货,包括我自己整理的一份最新的前端资料和零基础入门教程,送给大家,欢迎初学和进阶中的小伙伴

猜你喜欢

转载自blog.csdn.net/ZIYUSHUO/article/details/88582877