JavaScript中功能管道的简单说明
有时候我被问到为什么我们不再在RxJS中使用“点链”,或者为什么RxJS转向使用 管
。有很多原因,但这确实需要从更高层次上看待RxJS。
对管道功能的需求来自两个问题:希望为简单类型(如Array,Observable,Promise等)提供广泛的可用开发API,以及希望发布更小的应用程序。
尺寸问题
JavaScript是一种非常独特的语言,存在大多数其他编程语言所没有的问题:通常,JavaScript通过网络发布,解析并在用户想要使用JavaScript正在启动的应用程序的时刻执行。 JavaScript发送的越多,下载和解析所需的时间就越长,从而降低了应用的响应速度。可能对用户体验产生巨大影响的东西。
这意味着尝试保持JavaScript应用程序的小巧至关重要。幸运的是,我们现在有很多很棒的工具可以做到这一点。我们有很多“构建时”的捆绑器和优化器,它们可以执行诸如树摇动之类的事情,以便在构建时删除未使用的代码,因此我们可以向用户发送尽可能少的JavaScript。
不幸的是,如果不能静态地确保代码没有在某处使用,那么树抖动不会删除代码。
提供广泛的API
为了使类型尽可能有用,最好将一组结合良好的已知功能附加到该类型上。特别是通过在该类型上从左到右进行调用可以“链接”它。
JavaScript为内容提供广泛API的“内置”方式是原型扩充。这意味着您可以为任何给定类型添加方法 原型
宾语。所以如果我们想添加一个自定义 可能性
过滤到数组,我们可以这样做:
排列。原型。可能性 = 功能() { 返回 这个。过滤(X => X % 2 === 1) } 排列。原型。双 = 功能 () { 返回 这个。地图(X => X + X); } 排列。原型。日志 = 功能 () { 这个。的forEach(X => 安慰。日志(X)); 返回 这个; }
原型增强是有问题的
变异全局变量。你现在正在操纵别人可以触摸的东西。这意味着其他代码可以依赖于此开始 可能性
正在进行的方法 排列
,不知道它实际上来自第三方。这也意味着另一部分代码可以通过并践踏 可能性
用它自己的定义 可能性
。有这样的解决方案,比如使用 符号
,但它仍然不理想。
原型方法不能动摇。 Bundlers目前不会尝试删除已修补到原型上的未使用方法。有关推理,请参阅上文。捆绑者无法知道第三方是否依赖于使用该原型方法。
功能编程FTW
一旦你意识到了 这个
上下文实际上只是将另一个参数传递给函数的一种奇特方式,你意识到你可以像这样重写上面的方法:
功能 可能性(排列) { 返回 排列。过滤(X => X % 2 === 0); } 功能 双(排列) { 返回 ARR。地图(X => X + X); } 功能 日志(排列) { 排列。的forEach(X => 安慰。日志(X)); 返回 排列; }
现在的问题是你必须从右到左阅读你的阵列发生的事情,而不是从左到右:
//哎 日志(双(可能性((1, 2, 3, 4, 五))))
但是,优点是,如果我们不使用 双
,比方说,一个捆绑者将能够树摇动并删除 双
功能从最终结果发送给用户,使您的应用程序更小,更快。
管道从左到右的可读性
为了获得更好的从左到右的可读性,我们可以使用a 管
功能。这是一个常见的功能模式,可以通过一个简单的功能来完成:
function pipe(... fns){return(arg)=> fns.reduce((prev,fn)=> fn(prev),arg); }
这样做是返回一个新的高阶函数,它接受一个参数。返回的函数将参数传递给函数列表中的第一个函数, FNS
,然后获取结果,并将其传递给列表中的下一个函数,依此类推。
这意味着我们现在可以从左到右组合这些东西,这更具可读性:
管(可能性, 双, 日志)((1, 2, 3, 4, 五))
您还可以创建一个帮助程序,允许您将参数作为第一个参数提供,以使其更具可读性(如果可重用性更低),如下所示:
功能 pipeWith(ARG, ...FNS) { 返回 管(...FNS)(ARG); } pipeWith((1, 2, 3, 4, 五) 可能性, 双, 日志);
如果是 pipeWith
现在它将采用第一个参数,并将其传递给参数列表中紧跟其后的函数,然后它将获取结果并将其传递给参数列表中的下一个函数,依此类推。
带有参数的“可管理”函数
要创建一个可以通过管道输入但具有参数的函数,只需更高阶函数即可。例如,如果我们想制作一个 乘以
功能而不是 双
:
pipeWith((1, 2, 3, 4, 五) 可能性, 乘以(2) 日志); 功能 乘以(X) { 返回 (排列) => 排列。地图(ñ => ñ * X); }
组成
因为它只是函数,所以您可以使用简化代码并使其更具可读性 管
创建其他可重用,可管理的功能
常量 tripleTheOdds = 管(可能性, 乘以(3)); pipeWith((1, 2, 3, 4, 五) tripleTheOdds, 日志)
更大的JS生态系统和管道运营商
这与RxJS运营商通过Observable使用的模式大致相同 管
方法。这样做是为了解决上面原型中列出的所有问题。但这显然适用于任何类型。
而 原型
扩充可能是在JavaScript中向类型添加方法的“祝福”方式,在我看来,它有点像反模式。 JavaScript需要更多地开始接受这种模式,理想情况下,我们可以获得一个简单版本的管道运算符提议,以便在JavaScript中着陆。
使用管道运算符,上面的代码可能看起来像这样,但在功能上是相同的,并且不需要声明 管
帮手。
pipeWith((1, 2, 3, 4, 五) 可能性, 双, 日志); //变得 (1, 2, 3, 4, 五) |> 可能性 |> 双 |> 日志