<b>本文讲的是高阶函数(软件编写)(第四部分),</b>
Smoke Art Cubes to Smoke — MattysFlicks — (CC BY 2.0)
之前我们看到的 <code>.map()</code> 和 <code>.filter()</code> 都是高阶函数 —— 它们都接受一个函数作为参数,
先来看个一阶函数的例子,该函数会将单词数组中 4 个字母的单词过滤掉:
如果又要选择出所有以 's' 开头的单词呢?可以再定义一个函数:
显然可以看出这里面有很多重复的代码,这两个函数的主体是相同的 —— 都是遍历一个数组并根据给定的条件进行过滤。这便形成了一种特定的模式,可以从中抽象出更为通用的解决方案。
不难看出, “遍历”和“过滤”都是亟待抽象出来的,以便分享和复用到其他所有类似的函数中去。毕竟,从数组中选取某些特定元素是很常见的需求。
幸运的是,函数是 JavaScript 中的一等公民,就像数字、字符串和对象一样,函数可以:
像变量一样赋值给其他变量
作为对象的属性值
作为参数进行传递
作为函数的返回值
函数基本上可以像其他任何数据类型一样被使用,这点使得“抽象”容易了许多。例如,可以定义一种函数,将遍历数组并累计出一个返回值的过程抽象出来,该函数接收一个函数作为参数来决定具体的累计过程,不妨将此函数称为 reducer:
该 <code>reduce()</code> 接受 3 个参数:一个 reducer 函数、一个累计的初始值和一个用于遍历的数组。对数组中的每个元素都会调用 reducer,传入累计器和当前数组元素,返回值又会赋给累计器。对数组中的所有元素都执行过 reducer 之后,返回最终的累计结果。
在用例中,调用 <code>reduce</code> 并传给它 3 个参数:<code>reducer</code> 函数、初始值 0 以及需要遍历的数组。其中 <code>reducer</code> 函数以累计器和当前数组元素为参数,返回累计后的结果。
如此将遍历和累计的过程抽象出来之后,便可实现更为通用的 <code>filter()</code> 函数:
在此 <code>filter()</code> 函数中,除了以参数形式传进来的 <code>fn()</code> 函数以外,所有代码都是可复用的。其中 <code>fn()</code> 参数被称为断言(predicate) —— 返回一个布尔值的函数。
将当前值传给 <code>fn()</code>,如果 <code>fn(curr)</code> 返回 <code>true</code>,则将 <code>curr</code> 添加到结果数组中并返回之;否则,直接返回当前数组。
现在便可借助 <code>filter()</code> 函数来实现过滤 4 字母单词的 <code>censor()</code> 函数:
喔!将所有公共代码抽象出来之后,<code>censor()</code> 函数便十分简洁了。
<code>startsWithS()</code> 也是如此:
你若稍加留意便会发现 JavaScript 其实已经为我们做了这些抽象,即 <code>Array.prototype</code> 的相关方法,例如 <code>.reduce()</code>、<code>.filter()</code>、<code>.map()</code> 等等。
高阶函数也常常被用于对不同数据类型的操作进行抽象。例如,<code>.filter()</code> 函数不一定非得作用于字符串数组。只需传入一个能够处理不同数据类型的函数,<code>.filter()</code> 便能过滤数字了。还记得 <code>highpass</code> 的例子吗?
换言之,高阶函数可以用来实现函数的多态性。如你所见,相对于一阶函数而言,高阶函数的复用性和通用性更好。一般来讲,在实际编码中会组合使用高阶函数和一些非常简单的一阶函数。
<b></b>
<b>原文发布时间为:2017年4月19日</b>
<b>本文来自云栖社区合作伙伴掘金,了解相关信息可以关注掘金网站。</b>