在之前的一系列文章中,咱们解说了很多对于正则表达式的常识。那么作为一个前端工程师,如果想要把这些常识利用到咱们平时的开发中去的话,就须要晓得在JavaScript中,可能应用正则的函数有哪些?而后它们各自的性能是什么?有哪些须要留神的中央?只有把握好了每一个办法的应用场景,咱们才可能在须要应用的时候可能很快的想起来应用哪个办法效率最高,成果最好。

这些的确是一些根底的常识,然而我置信应该有很多同学还没有零碎的把这些常识学习一边。置信我,如果你可能把这篇文章看完的话,你必定能够学习到一些新的常识。晓得每一个办法的用处,应用场景,学会在适合的场景抉择适合的办法。当然你还可能把握这些办法须要留神的中央,以防在当前应用的时候陷入了窘境。

文章中的代码示例如果没有特地阐明的话,都是在Chrome浏览器中进行的。本篇文章的内容比拟长,倡议先珍藏起来,能够当前缓缓细看。

JavaScript中,可能应用正则表达式的函数有(排除了过期的办法):

  • RegExp.prototype

    • RegExp.prototype.test()
    • RegExp.prototype.exec()
  • String.prototype

    • String.prototype.match()
    • String.prototype.matchAll()
    • String.prototype.replace()
    • String.prototype.replaceAll()
    • String.prototype.search()
    • String.prototype.split()

RegExp.prototype

首先咱们要解说的是RegExp对象上的两个办法

RegExp.prototype.test()

  • 作用:检测给定的字符串中是否有满足正则的匹配
  • 代码示例

简略的匹配,依据匹配的后果确定是否匹配胜利。

const reg = /\d{4}-\d{2}-\d{2}/;const str1 = '2000-02-22';const str2 = '20-20-20';console.log(reg.test(str1)); // trueconsole.log(reg.test(str2)); // false

下面的正则表达式没有设置全局的标志符g,如果设置了全局的标志符的话,咱们在应用这个办法的时候就要小心一些了。因为如果正则表达式设置了全局的标识符g,那么对于同一个正则表达式来说,在运行test办法的时候,如果匹配胜利的话,它会批改这个正则对象的lastIndex属性,可能会在下次匹配的时候导致一些问题,咱们上面来看一个例子

const reg = /abc/g;const str1 = 'abcd';const str2 = 'abcdabcd';console.log(reg.lastIndex);  // 0console.log(reg.test(str1));  // trueconsole.log(reg.lastIndex);  // 3console.log(reg.test(str1));  // falseconsole.log(reg.lastIndex);  // 0console.log(reg.test(str2));  // trueconsole.log(reg.lastIndex);  // 3console.log(reg.test(str2));  // true

下面的例子很好地阐明了这种状况,如果咱们设置了全局标识符g的话,只有咱们以后的匹配是胜利的,那么接下来如果再次应用同样的正则进行匹配的话就可能会呈现问题,因为上一个胜利的匹配导致正则表达式对象的lastIndex属性的值产生了变动,那么下次进行匹配的时候是从lastIndex地位开始的,所以就可能会呈现一些问题

  • 注意事项:如果在应用test办法的时候,须要留神正则表达式是否带有g标识符。如果这个正则表达式须要进行屡次的匹配的话,最好不要设置g标识符。除非你晓得本人的确须要这样做。
  • 应用场景

如果有这样一个需要,你须要判断用户输出的用户名是否满足需要,需要如下:(1)用户名长度须要是8-16位。(2)用户名能够蕴含数字,字母(大小写都能够),下划线。(3)数字和字母是必须蕴含的

当然对于相熟正则表达式的你来说,这不是一个问题,能用一行代码解决的问题绝不用两行代码去解决。你能够很快能够通过应用test办法来解决这个问题。

const validNameRE = /^(?=_*(?:\d+_*[a-zA-Z]+|[a-zA-Z]+_*\d+))\w{8,16}$/;// 如果这是用户输出的用户名const userInputName = '1234567890';// 检查用户输出的用户名是否合乎要求const isValidName = validNameRE.test(userInputName); // false

在平时的开发中,如果须要判断页面所处的宿主环境的话,咱们也会应用test办法去判断以后页面所处的环境。例如,你须要判断以后页面所处的环境是不是iPhone的话,你可能会写出这样的判断:

const iPhoneReg = /iPhone/;console.log(iPhoneReg.test(navigator.userAgent));  // true

RegExp.prototype.exec()

  • 作用:这个办法是比拟罕用的一个办法,在给定的字符串中进行匹配,返回一个匹配的后果数组或者null。通常状况下咱们会应用这个办法来提取字符串中合乎匹配的一些字符串。
  • 代码示例

须要留神的是,如果没有合乎的匹配,返回的后果是null,而不是一个空数组[]。所以当咱们须要判断是否有匹配的后果的时候,不能凭感觉感觉返回的值是一个空的数组[]

const reg1 = /(\d{2}):(\d{2}):(\d{2})/;const str1 = 'Sat Aug 22 2020 17:31:55 GMT+0800 (中国规范工夫)';const str2 = 'Sat Aug 22 2020';console.log(reg1.exec(str1));  // ["17:31:55", "17", "31", "55", index: 16, input: "Sat Aug 22 2020 17:31:55 GMT+0800 (中国规范工夫)", groups: undefined]console.log(reg1.exec(str2));  // null

从下面的代码中咱们能够看到,如果没有匹配后果的话,返回的后果是null。如果可能匹配胜利的话,返回的后果是一个数组。在这个后果数组中,第0项示意正则表达式匹配的内容。其中第1..n项示意的是正则表达式中括号的捕捉内容,对于下面的示例来说,第1..3项示意的是捕捉工夫的时分秒。数组还有额定的属性indexinput,其中index示意正则表达式匹配到的字符串在原字符串中的地位。input示意原始待匹配的字符串。

  • 注意事项

    • 留神正则表达式是否设置了g标识符,如果设置了g标识符,那么咱们能够应用这个正则表达式进行全局的搜寻。能够看上面的代码示例。
    const reg = /\d/g;const str = '654321';let result;while ((result = reg.exec(str))) {  console.log(    `本次匹配到的数字是:${result[0]}, 正则表达式的 lastIndex 的值是:${      reg.lastIndex    }`  );}

输入的后果如下:

本次匹配到的数字是:6, 正则表达式的 lastIndex 的值是:1本次匹配到的数字是:5, 正则表达式的 lastIndex 的值是:2本次匹配到的数字是:4, 正则表达式的 lastIndex 的值是:3本次匹配到的数字是:3, 正则表达式的 lastIndex 的值是:4本次匹配到的数字是:2, 正则表达式的 lastIndex 的值是:5本次匹配到的数字是:1, 正则表达式的 lastIndex 的值是:6

须要留神的是,如果下面匹配的正则表达式没有设置g标识符,或者在while循环的条件判断中应用的是正则表达式的字面量的话,都会造成“死循环”。因为那样的话,每次循环开始的时候,正则表达式的lastIndex属性都会是0,导致result始终都是有值的,所以就导致了“死循环”。所以咱们在while循环中应用exec办法的时候肯定要小心一些

  • 应用场景:这个办法次要用来在原始文本中提取一些咱们想要的要害信息,所以只有是这样的一个需要场景,都能够应用正则表达式的exec办法去解决。比方:

    • 对用户输出内容中的链接进行自动识别,而后对相应的链接内容进行款式和性能上的解决。
    • 能够提取url中的查问参数,如果咱们须要本人把url中的查问参数提取进去的话,应用exec办法也是一个抉择。
    • 如果你浏览过vue的源码的话,在编译模块中的文本解析应用到了exec办法,有趣味的话大家能够看一看相干的代码实现。

当然还有很多的场景能够应用exec办法去解决的,大家在平时的开发中有没有应用过exec办法解决一些问题呢?能够在上面留言,咱们大家一起讨论一下,加深一下对这个办法的了解。

String.prototype

接下来咱们来解说一下String.prototype下面无关正则的一些办法。

String.prototype.match()

  • 作用:这个办法返回字符串匹配正则表达式的后果。
  • 代码示例
const reg = /\d/;const str = 'abc123';console.log(str.match(reg));  // ["1", index: 3, input: "abc123", groups: undefined]
  • 注意事项

    • 没有匹配到后果的返回后果是null
    const reg = /\d/;const str = 'abc';console.log(str.match(reg));  // null
    • 是否设置了g标识符,如果没有设置g的话,match的返回后果跟对应的exec的返回后果是一样的。如果设置了g标识符的话,返回的后果是与正则表达式相匹配的后果的汇合。
    const reg = /\d/g;const str = 'abc123';console.log(str.match(reg));  // ["1", "2", "3"]
    • 如果match办法没有传递参数的话,返回的后果是[''],一个蕴含空字符串的数组。
    const str = 'abc123';console.log(str.match());  // ["", index: 0, input: "abc123", groups: undefined]
    • 如果match办法传递的参数是一个字符串或者数字的话,会在外部隐式调用new RegExp(regex),将传入的参数转变为一个正则表达式。
    const str = 'abc123';console.log(str.match('b'));  // ["b", index: 1, input: "abc123", groups: undefined]
  • 应用场景

    简略获取url中的查问参数:

    const query = {};// 首先应用带有g标识符的正则,示意全局查找const kv = location.search.match(/\w*=\w*/g);if (kv) {  kv.forEach(v => {      // 应用不带g标识符的正则,须要获取括号中的捕捉内容    const q = v.match(/(\w*)=(\w*)/);    query[q[1]] = q[2];  });}

String.prototype.matchAll()

  • 作用:这个办法返回一个蕴含所有匹配正则表达式以及正则表达式中括号的捕捉内容的迭代器。须要留神的是这个办法存在兼容性,具体内容能够查看String.prototype.matchAll。
  • 代码示例
const reg = /(\w*)=(\w*)/g;const str = 'a=1,b=2,c=3';console.log([...str.matchAll(reg)]);

  • 注意事项

    • match办法雷同的中央是,如果传递给matchAll办法的参数不是一个正则表达式的话,那么会隐式调用new RegExp(obj)将其转换为一个正则表达式对象。
    • 传递给matchAll的正则表达式须要是设置了g标识符的,如果没有设置g标识符,那么就会抛出一个谬误。
    const reg = /(\w*)=(\w*)/;const str = 'a=1,b=2,c=3';console.log([...str.matchAll(reg)]);  // Uncaught TypeError: String.prototype.matchAll called with a non-global RegExp argument
    • 在能够应用matchAll的状况下,应用matchAll比应用exec办法更便捷一些。因为在全局须要匹配的状况下,应用exec办法须要配合循环来应用,然而应用matchAll就能够不应用循环。
    • matchAll办法在字符串执行匹配的过程中,正则表达式的lastIndex属性不会更新。更多详情能够参考String.prototype.matchAll()。
  • 应用场景

还是以下面的获取url中的查问参数这个小性能来实际一下:

const query = {};const kvs = location.search.matchAll(/(\w*)=(\w*)/g);if (kvs) {    for (let kv of kvs) {        query[kv[1]] = kv[2];    }}console.log(query);

String.prototype.replace()

  • 作用:这个办法在平时的开发中应该比拟罕用,那么它的作用就是应用替换物replacement替换原字符串中合乎某种模式pattern的字符串。其中替换物能够是一个字符串,或者返回值是字符串的函数;模式能够是正则表达式或者字符串。
  • 代码示例

    因为这个函数的入参能够是不同的类型,所以对每种类型的入参咱们都来实际一下吧。

    • pattern是字符串,replacement也是字符串。这种模式在平时的开发中应用的比拟多。
    const pattern = 'a';const replacement = 'A';const str = 'aBCD';console.log(str.replace(pattern, replacement));  // ABCD
    • pattern是正则表达式,replacement是字符串
    const pattern = /__(\d)__/;const replacement = "--$$--$&--$`--$'--$1--";const str = 'aaa__1__bbb';console.log(str.replace(pattern, replacement));  // aaa--$--__1__--aaa--bbb--1--bbb

如果replacement是字符串,那么在这个字符串中能够应用一些非凡的变量,具体可参考Specifying a string as a parameter。

  • pattern是正则表达式,replacement是函数

    const pattern = /__(?<number>\d)__/;const replacement = function(match, p1, offset, str, groups) {  console.log(`匹配到的字符串是:${match}\n捕捉到的内容是:${p1}\n匹配的地位是:${offset}\n原始待匹配的字符串是:${str}\n命名的捕捉内容是:${JSON.stringify(groups)}`);  return '======';};const str = 'aaa__1__bbb';console.log(str.replace(pattern, replacement)); // aaa======bbb

其中控制台的输入如下所示:

匹配到的字符串是:__1__捕捉到的内容是:1匹配的地位是:3原始待匹配的字符串是:aaa__1__bbb命名的捕捉内容是:{"number":"1"}

如果你对replacement是函数这种状况不是很理解的话能够看看Specifying a function as a parameter,外面会有具体的解释,这里就不在具体解释了。

  • 注意事项

    须要留神的中央就是当咱们的pattern是正则表达式的时候,要留神是否设置了g标识符,因为如果没有设置g标识符的话,只会进行一次匹配。设置了g标识符的话,会进行全局的匹配。

  • 应用场景

    对于前端来说,对用户的输出进行校验时很常见的需要。如果咱们有一个输入框,只容许用户输出数字,咱们能够这样解决:

    const reg = /\D/g;const str = 'abc123';console.log(str.replace(reg, ''));  // 123

    这样就可能保障用户的输出只有数字了。

String.prototype.replaceAll()

As of August 2020 the replaceAll() method is supported by Firefox but not by Chrome. It will become available in Chrome 85.

这个办法和replace办法的作用差不多,从名字上就可能晓得replaceAll是全局的替换。因为这个办法的兼容性问题,咱们须要在Firefox浏览器上进行试验。

const pattern = 'a';const replacement = 'A';const str = 'aBCDa';console.log(str.replace(pattern, replacement));  // ABCDaconsole.log(str.replaceAll(pattern, replacement));  // ABCDA
  • 注意事项:如果给函数传递的pattern参数是个正则表达式的话,这个正则表达式必须设置了g标识符,不然会抛出一个谬误。
const pattern = /a/;const replacement = 'A';const str = 'aBCDa';console.log(str.replace(pattern, replacement));  // ABCDaconsole.log(str.replaceAll(pattern, replacement));  // Uncaught TypeError: replaceAll must be called with a global RegExp

String.prototype.search()

  • 作用:这个办法用来在字符串中寻找是否含有特定模式的匹配,如果找到对应的模式,返回匹配开始的下标;没有找到的话返回-1
  • 代码示例
const reg = /\d/;const str1 = '123';const str2 = 'abc';console.log(str1.search(reg));  // 0console.log(str2.search(reg));  // -1
  • 注意事项

    • 如果传入的参数不是一个正则表达式的话,会隐式的调用new RegExp(regexp)将其转换为一个正则表达式。
    • 没有找到相应匹配的时候,返回的值是-1;所以大家在应用这个办法做判断的时候要留神,只有返回值是-1的时候,才示意没有找到相应的匹配。
  • 应用场景

如果你须要找到特定匹配在字符串中的地位的话,那么能够应用search办法。

const reg = /\d/;const str = 'abc6def';console.log(str.search(reg));  // 3

String.prototype.split()

  • 作用:将一个字符串依照分割器进行宰割,将宰割后的字符串片段组成一个新的数组,其中分割器separator能够是一个字符串或者一个正则表达式。
  • 代码示例

    • 分割器separator是字符串:
    const str = 'hello, world!';console.log(str.split(''));  // ["h", "e", "l", "l", "o", ",", " ", "w", "o", "r", "l", "d", "!"]
    • 分割器separator是正则表达式:
    const str = 'abc1abc2abc3';const separator = /\w(?=\d)/;console.log(str.split(separator));  // ["ab", "1ab", "2ab", "3"]
  • 注意事项

    • 如果split办法没有传递参数的话,会返回一个蕴含原字符串的数组:
    const str = 'hello, world!';console.log(str.split());  // ["hello, world!"]
    • 因为JavaScript的字符串是应用UTF-16进行编码的,该编码应用一个16比特的编码单元来示意大部分常见的字符,应用两个编码单元示意不罕用的字符。所以对于一些不罕用的字符来说,在应用split办法进行字符串宰割的时候可能会呈现一些问题:
    const str = '????????????????????????';console.log(str.split(''));  // ["�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�"]

如何解决这种类型的问题呢?第一种办法是应用数组的扩大运算符:

const str = '????????????????????????';console.log([...str]);  // ["????", "????", "????", "????", "????", "????"]

第二种办法是应用设置了u标识符的正则表达式:

const str = '????????????????????????';const separator = /(?=[\s\S])/u;console.log(str.split(separator)); // ["????", "????", "????", "????", "????", "????"]
    • 如果传入的正则表白参数中含有捕捉的括号,那么捕捉的内容也会蕴含在返回的数组中:
    const str = 'abc1abc2abc3';const separator = /(\w)(?=\d)/;console.log(str.split(separator));  // ["ab", "c", "1ab", "c", "2ab", "c", "3"]
    • split办法还能够传入第二个参数,用来管制返回的数组的长度:
    const str = 'hello, world!';console.log(str.split('', 3));  // ["h", "e", "l"]
    • 应用场景

    在理论的开发中,最罕用的场景就是将一个字符串转换为一个数组了:

    const str = 'a/b/c/d/e';console.log(str.split('/')); // ["a", "b", "c", "d", "e"]

    总结

    当咱们可能把下面的这些办法都纯熟的把握之后,那么在理论的开发中再联合正则表达式来应用的话,那几乎就是锦上添花,可能在一些场景下进步咱们开发的效率。

    当然光靠看看文章是不可能很好地将这些知识点都记牢固的,你须要的是一个一个的实际一下,这样才可能加深本人的记忆,才可能记得更牢固

    如果大家还想理解更多对于正则表达式的知识点的话,能够看看我之前写的一系列的文章:

    • 正则表达式匹配素数的原理解说
    • 间隔弄懂正则的环视,你只差这一篇文章
    • 正则表达式量词匹配形式的解说(上篇)
    • 想写出效率更高的正则表达式?试试固化分组和占有优先匹配吧

    如果你对本篇文章有什么意见和倡议,都能够间接在文章上面留言,也能够在这里提出来。也欢送大家关注我的公众号关山不难越,学习更多实用的前端常识,让咱们一起致力提高吧。