正则表达式仪式:从理论到实践
#javascript #regex #dom

JavaScript in Plain English logo

本文首次在JavaScript in Plain English上发表。

第2部分:应用理论构建一个验证邮政编码的演示单页应用程序。

Photo of woman's hands doing system design sketch

kelly Sikkema在Unsplash
照片

目录的表


欢迎回来!

对于那些阅读了本教程系列第一部分的人,感谢您通过一篇理论文章掌握。随着我们从理论进行实践,它将获得回报。

在第2部分中,我们应用了我们在第1部分中学到的知识, 正则表达式为Web开发人员 ,这对某些关键概念进行了快速介绍正则表达式。

我们将通过构建演示单页应用程序(SPA)将理论转化为实践。


入门

对于那些尚未看到我们的水疗中心的人,下面的图1 显示了着陆页:

Fig. 1 Application Landing Page

图。 1申请着陆页

上面的开放屏幕显示以下组件:

1.主要标题,它说明了该应用程序所做的工作,
'

2.在此下方,标注框在此范围内说明了
的目的。

更详细地应用。

3.接下来是多行测试字符串的滚动列表,将传递给

'它。

ââââ€4.在此下方,我们有一个 widtget ,在左侧,有一组2
'堆叠按钮。最高的验证邮政编码,触发
''– – – – – – – – – – – – – –'''''''''''''''''''''字符串。单击时,右侧的窗口将带有有效的zip

'''''''''代码正则表达式。。

5.第二个按钮 将文本框重置为默认状态,如
' 图1

我们将如何做所有这些? 让我们考虑一下。

我们向我们的应用程序提供了一些预制的测试数据(尽管用户将文件上传到使用测试数据,则该原理将保持不变)。无论哪种情况, REGEX引擎均使用测试数据显示,并要求仅用于有效的邮政编码返回匹配项。

这比听起来更复杂正则表达式指定为 Regex Engine 必须能够执行以下所有操作:

  • 确保用户输入仅由数字组成,并且可选为+4邮政编码的连字符。 (显然,在此演示应用程序的情况下,没有用户输入。但是,该项目的进一步开发可以使应用程序完全交互。)

  • 没有字母顺序,标点符号或其他特殊符号保存在+4邮政编码中所需的连字符可能是返回的匹配项的一部分。

  • 确保没有其他字符先于或遵循各种邮政编码(5位或+4)。

  • 在每种情况下,邮政编码都必须以5位数字开始。

  • 可选, Regex Engine 必须能够匹配5个字符序列,以连字符开始,并以4个数字结束。

  • 最后,最后4位数字后不得发生字符。

所有这一切似乎是一个艰巨的任务。但是,正如我们在上一个教程中看到的那样,正则表达式具有满足所有这些要求的能力。

这一刻终于到了!


让我建立它!

毕竟,让我们付诸实践并构建此应用程序。

开始,请下载starter code

步骤1.解开启动代码存档

存档命名为 Regex-Starter-code.zip 您可以将其重命名为您的喜好,例如 Regex-demo-web-app.zip.zip 。确保将 .zip 扩展在名称中,因为如果缺少扩展名,则某些邮政编码可能无法识别文件。

出于本教程的目的,我使用Microsoft Visual Studio Code,但是您也可以使用您喜欢的文本编辑器。

一旦您解开了重命名的 Regex-demo-web-app.zip (或您选择的任何名称),您将拥有类似于 figif中所示的文件结构2

Fig. 2 Project File Structure

图。 2项目文件结构

步骤2.将项目部分放在一起

我们的项目由 html 内容组成, css 样式和单个 javascript fileâ app.js 。

尽管我为您提供了样式,因此我们可以主要关注 javascript ,但我们将从 index.html 文件提供的裸露结构开始。

让我们首先打开您喜欢的Web浏览器中的应用程序 index.html 文件。为了本教程的目的,我将 Google Chrome 用于其出色的开发人员工具。 图3 下面显示了我们的准骨
应用程序框架。

Fig 3. Bare Bones HTML

图。 3.裸骨html

代码清单1 下面显示了我们 index.html 文件的片段。


<!-- INSERT link to zipcodes.css stylesheet. -->

<title>Regex Demo-Zip Code</title>

<!-- INSERT app.js JavaScript -->

</head>
代码清单1. index.html显示相关 content

我们现在将用将我们应用程序的三个组件编织在一起的代码替换评论行。

您知道,为了使您的浏览器按照我们的意愿呈现申请点目标页面,我们必须将我们的级联样式表链接到 html file。 p>

我们还必须将我们的JavaScript代码链接到 html 文件。按照以下步骤:

1.开放 index.html 如果您还没有这样做。
2.搜索上面的列表1 中的第一个评论,并用
''''''' - 3.现在我们整合了项目的最后一个组成部分。搜索
代码列表中的第二条评论
上面的第二条评论,然后用
''''' :
-

<script>标签中的关键字 defer 确保浏览器

''加载对于 index.html 脚本执行之前的文档

开始。这样可以防止 javascript 丢弃错误,因为
'但已定义。

'

âââ€4.刷新您的浏览器。

太好了!如果一切都按计划进行,则您的网页应完全如图1 所示。但是,如果您单击两个按钮, 在这一点上绝对什么也没发生。 显然,这是因为我们尚未编写任何提供功能的代码这些按钮。

步骤3.捕获必要的dom节点

首先打开 app.js 启动代码存档中包含的文件。

在这一点上,整个文件只包含评论。我们首先捕获必要的 dom节点。我们将使用文档对象模型(DOM)来查明屏幕的各个部分,这些部分在用户与应用程序交互时需要更改。

首先需要在DOM对象中捕获哪些HTML元素?需要访问这些节点。因此,我们需要2 dom对象才能捕获验证邮政编码 reset 按钮。

接下来我们需要两件事。我们需要在按钮的右侧默认消息,该按钮现在读取 no Match 消失,并用 Regex Engine返回的匹配列表替换。

为了做到这一点,我们需要能够捕获默认消息,以便可以将其删除。我们还需要访问其父元素,一旦删除默认消息,它将包含滚动列表。因此,我们需要2 dom对象捕获这些元素。

总的来说,我们必须声明4 dom对象才能捕获节点参考我们需要访问的 html元素。 p>

图4 下面显示了 html 的相关行,其中包含我们将在 dom对象中捕获的元素


Fig. 4 Elements to Be Captured in the DOM

图。在dom
中捕获的4个元素

中的关键标记图4 是按钮,段落读取“不匹配,及其父元素” <div>带有 id 的属性

每个元素在其上设置了 ID属性,因此可以使用document.getElementById()方法捕获它。还有其他手段可以使用,例如querySelector()querySelectorAll()方法。

对于本教程,我选择使用getElementById()的直接性和简单性。

要创建必要的DOM对象,在入门代码中搜索读取// TO BE DONE: Declare necessary DOM nodes的行并将其替换为以下4行代码:


const resultButton = document.getElementById(validate);
const resetButton = document.getElementById(reset);
const resultBox = document.getElementById(results);
const placeHolder = document.getElementById(message);
代码列表2:捕获所需的DOM节点
的代码

仍然,如果刷新浏览器并单击验证邮政编码按钮,则不会发生任何事情。 我们尚未提供此功能。我们现在要做的就是使用 Google Chrome开发人员工具


验证节点是否可见到DOM

要执行此操作,刷新您的浏览器并通过按 ctrl+shift+shift+j windows/linux/unix和 command+command+option+Option javascript控制台 J Mac。您应该在下面看到图5 之类的东西:


Fig. 5 Google Chrome Showing JavaScript Console on Right

图。 5 Google Chrome在右侧显示JavaScript控制台

要验证我们的节点对象,请单击 javascript控制台中的插入点并键入以下命令,然后是 Enter/return/return/return 键:


resultButton <Enter>
resetButton <Enter>
resultBox <Enter>
placeHolder <Enter>

请确保通过单击折叠信息旁边的正确点箭头来扩展 Resultbox
您应该在下面看到图6 中显示的结果:


Fig. 6 Browser with JavaScript Console Confirming Access to the Desired Nodes

图。 6带JavaScript控制台的浏览器确认访问所需的节点

到目前为止,我们已经进入了应用程序中的切入点,因为我们可以访问需要操纵的页面部分。接下来,我们需要声明我们的测试字符串和我们的正则表达式


步骤4.声明测试字符串和正则表达式

我们需要声明两个常数一个将存储测试字符串我们传递给我们的正则表达式和一个存储正则表达式对象将通过测试字符串

在您的入门代码中搜索读取// TBD: Declare testString的行并将其替换为以下代码:


const testString =
`10003
asdf10003
10003asdf
jklm10003^$@%
10003-8924
one zero zero zero three
9101-94015
94015-9101
20012
08735
KbdsD$%^&*
sSd070031jkl;m
70122
\sKu2034
98108
75381
asdfjkl;
60661
!9004!@#$5^&*
97218
#,.$$&&%@
10022-3337
eNuhfF!.` ;
代码列表3.测试字符串

在您的入门代码中搜索读取

的行

// TO BE DONE: Declare regular expression.

并将其替换为以下代码:


const regex = /^[0-9]{5}(-[0-9]{4})?$/gm;
代码清单4.声明正则表达式

为我们的演示应用程序设置了阶段。我们已经捕获了 dom节点,当用户单击验证邮政编码按钮时,必须将动态数据注入页面。我们还提供了我们的测试弦常数和我们的 Regex 常数。

接下来,我们必须继续构建功能,该功能将提供我们想要在页面上注入的有效邮政编码的匹配。


步骤5.使用Regex返回有效的邮政编码Matches

在我们实际添加必要的代码为 app.js 之前,让我们退后一步以清楚了解匹配过程将如何工作。

我们已经有了我们的正则> 对象,该对象使用对象文字符号在上面声明。该对象公开方法执行不同种类的搜索。

REGEX对象暴露了几种方法。您可以在Mozilla开发人员网络regular expressions page上找到它们(“使用JavaScript中的正则表达式” )。但是,与我们相关的是match()方法。此方法的语法是:


<string object>.match(<regular expression object>);


我们的字符串对象是常数测试串match() 方法反过来采用一个参数,那就是正则表达式对象

match()的返回值是 array 正则表达式返回的每场比赛组成。

返回值必须返回到某物,以便可以访问它。在上面显示的语法中,对象match() 方法返回的 是无法解决的,尽管存储器已保留。

一旦执行方法 javaScript will 垃圾收集一旦确定尚未使用或正在使用的值不再使用。如果您有兴趣了解有关此信息的更多信息,则可能需要咨询此简短的文章,但有关garbage collection的信息丰富的文章(垃圾收集)。

我们将采用的实际语法如下所示:


const myConstant = <string.object>
     .match(<regular expression object>);

在您的入门代码中搜索读取

的行

// TO BE DONE: Save regex match to matches variable.

并将其替换为:


const match = testString.match(regex);
代码列表5.执行正则匹配

这一行代码实现了很多。在一口气中, teststring 将其传递给 REGEX (我们的正则表达式对象),进行模式匹配,提取有效的邮政编码,结果存储在 array 中,匹配> 的标识符。 非常有力的东西。


现在让我们通过采取以下步骤来测试以下步骤:

1. 1.刷新您的浏览器。如果您尚未打开 javascript
'âââ€ââ€',请现在这样做。

2.单击 javaScript控制台和type 匹配
''的插入点输入/返回键。一旦扩展了内容

在控制台中返回的内容,您的显示器应该看起来像
'''''图7
下面:


Fig. 7 Browser with JavaScript Console confirming valid zip code matches

图。 7带有JavaScript控制台的浏览器确认有效的邮政编码匹配

作为图7 确认有11个有效的邮政编码匹配正则表达式对象

显然,我们不能要求最终用户输入JavaScript控制台,以获取此验证过程的结果。使这是一个真正的动态应用程序,我们必须构建一些准备基础架构才能为创建两个事件听众一个单击验证邮政编码按钮,另一个单击 reset 按钮。

步骤6.创建助手functions

该基础架构包括什么? 如果我们从最明显的假设开始,当最终用户单击任何一个按钮时,我们需要发生一些事情。 /p>

我们的应用程序是事件驱动的。因此,我们需要为按钮编写两个事件听众。正如我们将看到的那样,这些事件听众由几个辅助功能提供动力。

在我们编码第一个助手之前,我们需要声明一个空数组,其代码将填充。

在您的入门代码中搜索读取

的行

// TO BE DONE: Declare empty array for text nodes.

并将其替换为:


const paragraphText = [];
代码清单6.声明段落文本数组

上面的空数组将由我们的第一个助手函数填充:


createParagraphText()助手函数

在您的入门代码中搜索读取

的行

// TO BE DONE: Code createParagraphText() helper function.

并将其替换为:


const createParagraphText = () => {
  matches.forEach( match => {
    let matchNumber = matches.IndexOf(match) + 1;
    paragraphText.push(
      ` Match #${matchNumber}: ${match}`
    );
  };
};
代码清单7. createParagraphText()助手函数

此辅助功能中发生了什么? 让它按行分解。

createParagraphText()中的代码由forEach()循环组成。 forEach() array 对象暴露。在上面的代码中,我们的 array 匹配常数。

我们称forEach() 方法 匹配匹配方法的参数部分中找到的的参考是对匹配中的每个元素的引用阵列,它循环。

还有一个匿名回调函数作为第二个参数,它从上面的match()方法创建的matches[]数组中提取数据。在回调函数的第一行中,我们声明变量 matchnumber ,该变量在matches[]数组中存储 index + 1 的值。

我们称匹配> 上的indexOf()获取阵列索引 匹配 1 被添加到索引值中,因为众所周知,数组是 0-索引,对于人类来说,参考更有意义匹配 1至11 ,而不是 0至10

在我们功能的最后一行中,我们使用push() 方法 array array array aray对象。。 p>

我们指定要推到每个项目的数组的数据,都使用模板符号和字符串插值来创建一个看起来像这样的数组元素:


Match #1: 10003

您可能想知道为什么我没有创建完整的段落。

在我们构建它之前,我们需要声明另一个空 array 以包含完整的段落。

在您的入门代码中搜索读取

的行

// TO BE DONE: Declare paragraphs array to hold complete paragraphs.

并将其替换为:


const paragraphs = [];
代码清单8.声明段落[]数组

paragraphs[]阵列将由完整的段落填充,从paragraphText[]阵列中提取的子文本节点。

createParagraphs() 函数

在您的入门代码中搜索读取

的行

// TO BE DONE: Code createParagrahs() helper function.

并将其替换为:


const createParagraphs = () => {
  paragraphText.forEach( text => {
    const paragraph = document.createElement(p);
    paragraph.textContent = text;
    paragraphs.push(paragraph);
  };
};
代码列表9. createParagraphs()助手函数

那么,此代码到底在做什么? createParagraphs()函数的整个逻辑是在单个呼叫forEach()方法中找到的。

循环中的第一行是:

const paragraph = document.createElement(p);

让我们考虑一下这里发生的事情。

每次通过循环, javascript 找到文本节点paragraphText[]数组的成员。这条代码线声明了文本节点将以 child>。

循环中的下一行是:


paragraph.textContent = text;

此行将当前索引处的文本节点附加到上一行中创建的<p>元素。

循环中代码的最后一行是:


paragraphs.push(paragraph);

这条代码线将完整的段落推向paragraphs[]数组。重复此序列,直到paragraphText[]数组中的最后一个元素已读取为循环。

现在,完整的段落数组等待将其附加到 resultbox 对象。最终的辅助功能将完成此任务。


appendParagraphs() helper函数

让S回顾图1 暂时。所示的两个按钮必须在其右边的一个盒子上,带有读取 no Match>的消息。

我们现在想交换此框的内容,将无匹配占位符消息带有一个滚动窗口,该窗口将包含其子女的所有段落中的所有段落。

在您的入门代码中搜索读取

的行

// TO BE DONE: Code createParagrahs() helper function.

并将其替换为:


const appendParagraphs = (parent, children) => {
  children.forEach(  (child) => {
    parent.appendChild(child);
  });
};
代码清单10.附录Paragraphs()助手功能

在仅5行代码中,appendParagraphs()完成了很多工作。该功能需要两个参数 parent children

调用此函数时,parent是指resultbox,rustax box,即在我们的 index.html 文件中捕获<div id=“results”>的节点对象。孩子们指的是paragraphs[]阵列。

我们的最终辅助功能还使用forEach()方法。在这里,paragraphs[]数组的所有元素都附加到 resultbox节点,重复此过程,直到将所有11个匹配都添加到 Resultbox


重要: 如果您尝试将这些节点记录到 JavaScript Console 中,您将收到一条错误消息。这是因为每个 助手函数 我们创建了到达这一点,这是由 回调函数调用 事件听众 我们尚未编码。


我们现在已经完成了由数组和3个辅助功能组成的基础架构。


一个重要的旁边: 您会注意到,在 appendParagraphs() 函数中,我们具有一个完全抽象的函数,该功能是 de-从我们的应用程序中的数据集中耦合


尽管为了简短的利益,我可以为所有辅助功能完成此示例。
de耦合的技术 从它们操作的数据中函数使代码更加可重复使用,并且通常是公共应用程序发布中的实践。 <
< /p>


接下来是什么?

成功完成了上述所有步骤后,您可以继续以第三个和结论的结论 正则表达式''仪式:完成申请。