重构javascript_重构javascript集合管道

重构javascript

This is the first post in a series of posts about refactoring your Javascript code to use map, filter and reduce instead of loops. I refer to this style of programming as Collection Pipelines as I was first introduced to this concept in Martin Fowler’s Refactoring.

这是有关重构Java代码以使用map,filter和reduce而不是loops的系列文章中的第一篇。 在Martin Fowler的Refactoring中 ,我首次将这种编程风格称为Collection Pipelines

One of the first control structures we learn as programmers is loops. In Javascript, loops still tend to be the default way for people to process collections, despite the language having excellent support for higher order functions (fancy name for functions that take a function as an input and returns another function) such as map(), filter() and reduce().

程序员是我们学习的最早的控制结构之一。 在Javascript中,循环仍然是人们处理集合的默认方式,尽管该语言对高阶函数(将函数作为输入并返回另一个函数的函数的奇特名称)有出色的支持,例如map(), filter()和reduce()。

I’ve worked on some truly gnarly code over the years — some of it was even written by other people. A recurring theme I have noticed is that a lot of complexity arises from using loops to iterate over a collection of objects. What begins as an innocent looking loop, somehow mutates into some beast of complexity with nested conditionals, variable references 20 lines up the page, and pyramid style code of nested logic. Perhaps the most nefarious risk in using loops is what I refer to as Responsibility Creep. A piece of code starts with a single, narrow responsibility and over time developers add “just a little bit more” logic to handle some new edge case or data flow path. A year later and your innocent loop has turned into a true monster.

这些年来,我一直在研究一些真正粗糙的代码-其中一些甚至是由其他人编写的。 我注意到一个反复出现的主题是,使用循环迭代对象集合会产生很多复杂性。 它从一个无害的外观循环开始,以某种方式转变为嵌套条件的复杂性,可变引用在页面上排成20行,并嵌套了金字塔样式的代码。 使用循环的最大危害也许就是我所说的责任蠕变 。 一段代码从一个单一的,狭窄的职责开始,随着时间的推移,开发人员增加了“多一点”的逻辑来处理一些新的边缘情况或数据流路径。 一年后,您的无辜循环变成了一个真正的怪物。

Throwing the “for loop” in the bin, and forgetting about it will make your code a lot easier to read and maintain. You do pay a penalty on performance for collections with millions of records but for 99% of the code most people write, these micro-optimisations aren’t worth the trade off in maintenance and readability.

将“ for循环”扔到垃圾箱中,而忽略它会使您的代码更易于阅读和维护。 对于具有数百万条记录的集合,您的确要为性能付出代价,但是对于大多数人编写的99%的代码,这些微优化不值得在维护和可读性之间进行权衡。

As an exercise I went and searched every bit of Javascript and Typescript I’ve written over the last 18 months or so, and there is not a single for loop anywhere! There’s also not a single use of the words class or function (except for third party modules — or where enforced by APIs (e.g. Vue)) — but that’s a series on its own.

作为练习,我去搜索了过去18个月左右编写的Javascript和Typescript的每一部分,而且在任何地方都没有一个for循环 ! 也没有单独使用函数一词(第三方模块除外-或由API强制执行(例如Vue))-但这只是一个系列。

Refactoring your code to use collection pipelines can take a bit of a change of thought process, and often people will claim that it is impossible for tasks that they are doing. I’ve yet to find a single loop (nested or otherwise) that couldn’t be refactored to use map, filter or reduce. To help illustrate this point I am going to take simple LeetCode/HackerRank challenges, grab the highest rated solution that uses loops, and refactor all the loops out of it. I’ll also show some real world examples of actual production code to show how readability can be improved (algorithms are notoriously messy). If you have an example snippet of code you’d like refactored just drop it in the comments section and i’ll show you how it can be refactored.

重构代码以使用收集管道可能会改变思维过程,而且人们通常会声称不可能完成他们正在执行的任务。 我还没有找到无法重构为使用map,filter或reduce的单个循环(嵌套循环或其他循环)。 为了帮助说明这一点,我将接受一些简单的LeetCode / HackerRank挑战,抓住使用循环的最高评价解决方案,并从中重构所有循环。 我还将展示一些实际生产代码的真实示例,以展示如何提高可读性(众所周知,算法很混乱)。 如果您有一个示例代码片段想要重构,只需将其放在注释部分中,我将向您展示如何重构它。

示例1:IPv4验证 (Example 1: IPv4 validation)

We’ll take a simple function that takes a string and returns true if it is a valid IPv4 and false if it isn’t. IP addresses are 4 bytes long and each byte can only store the decimal values of 0–255.

我们将使用一个简单的函数,该函数采用一个字符串,如果它是有效的IPv4,则返回true,否则返回false。 IP地址长4个字节,每个字节只能存储0-255的十进制值。

// Using Loops (and Regex)


const validateIP = ip => {
  const regex = /^\d+$/;
  const s = IP.split('.');
  if(s.length !== 4) return false;


  for(let i = 0; i < s.length; i++) {
    if(!regex.test(s[i])) return false;
    if(s[i].length > 1 && s[i].startsWith('0')) return false;


    const n = parseInt(s[i]);
    if(Number.isNaN(n)) return false;


    if(n < 0 || n > 255) return false;
  }
  return 'IPv4';
}
    
    
// Using a filter and map
const validRange = (n: number): boolean => n >= 0 && n <= 255;


const validateIP = (ip: string): boolean => {
  const numbers: string[] = ip.split('.');
  return numbers.length === 4
    && numbers
      .filter((x) => Number(x).toString() === x) // remove any numbers with leading 0s
      .map((x) => parseInt(x, 10)) // convert to number
      .filter(validRange) // filter out invalid range
      .length === 4; // check we still have 4 numbers
};

Our first example uses a traditional loop — it looks like a very traditional implementation and what I see in most code in the wild. This example actually isn’t that bad, the logic is mostly self contained and we aren’t mutating variables outside of the loop (e.g. pushing to a results array). It mostly reads top to bottom and the indexing isn’t too confusing.

我们的第一个示例使用传统循环-看起来像是非常传统的实现,以及我在大多数代码中看到的。 这个例子实际上还不错,逻辑大部分都是自包含的,并且我们不在循环外对变量进行突变(例如,推入结果数组)。 它通常从上到下读取,索引也不太混乱。

Below this I present a solution using a pipeline of collection methods (filter and map). It reads top to bottom and it is very explicit in intent. More importantly it is not susceptible to Responsibility Creep. We have a collection, we filter it, map it and filter it again — at each stage piping the result from the previous step into the next step. It’s a much more obvious task to hijack the responsibility of this pipeline and add new functionality.

在此之下,我提出了一个使用收集方法(过滤器和映射)管道的解决方案。 它从上到下阅读,意图很明确。 更重要的是,它不受责任蠕变的影响。 我们有一个集合,我们对其进行过滤,映射和再次过滤-在每个阶段,将上一步的结果传递到下一步。 劫持该管道的责任并添加新功能是一项更为明显的任务。

The astute reader will notice a few things about the second solution. Firstly, we now iterate up to 12 times instead of 4. Each filter and map iterates over the entire collection, and passes the result to the next step. In this case we don’t really notice the overhead, but in a larger collection we would (or if we had more steps in the pipeline). We’ll cover how to optimise this later.

精明的读者会注意到有关第二种解决方案的一些注意事项。 首先,我们现在最多迭代12次(而不是4次)。每个过滤器和映射都迭代整个集合,并将结果传递到下一步。 在这种情况下,我们并没有真正注意到开销,但是在更大的集合中,我们会(或者如果我们在管道中有更多步骤)。 稍后我们将介绍如何对其进行优化。

You may prefer the first solution, you may think my points are trivial and conceited, however my main aim for this series is to show that ALL loops CAN be replaced with collection pipelines. I’ll leave it up to you to decide which you prefer.

您可能更喜欢第一个解决方案,您可能认为我的观点微不足道,很自负,但是本系列的主要目的是表明可以用收集管道替换所有循环。 我将由您决定自己喜欢哪个。

For more examples you can save this post, and i’ll update it with links to new posts as I release them.

有关更多示例,您可以保存该帖子,并在发布它们时使用指向新帖子的链接对其进行更新。

I plan on releasing a number of examples in this series. If you want to learn HOW to practically refactor existing code, keep an eye out for my upcoming e-book that will step through exactly how to do this, and will include many more real world examples and applications. You can pre-order the book for just $10 here

我计划在本系列中发布许多示例。 如果您想学习如何实际重构现有代码,请密切注意我即将发布的电子书,该书将逐步介绍如何执行此操作,并将包括更多实际示例和应用程序。 您可以在这里以$ 10的价格预订这本书

Here’s a list of the next few articles to come this series

这是本系列接下来的几篇文章的清单

There will also be a handful of algorithms such as

也将有一些算法,例如

  • Conways Game of Life

    康威人生游戏
  • Largest sub-array in an array

    数组中最大的子数组
  • The Sieve of Eratosthenes

    Eratosthenes筛
  • Others

    其他

Real world examples

现实世界中的例子

  • Queue Processing

    队列处理
  • Request/Response transformation

    请求/响应转换
  • Data Mapping

    资料对应
  • Others

    其他

翻译自: https://medium.com/@Michael_Timbs/refactoring-javascript-collection-pipelines-3ebc2e63abee

重构javascript

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值