乐趣区

关于前端:想要在JS中把正则玩得飘逸学会这几个函数的使用必不可少

在之前的一系列文章中,咱们解说了很多对于正则表达式的常识。那么作为一个前端工程师,如果想要把这些常识利用到咱们平时的开发中去的话,就须要晓得在 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)); // true
console.log(reg.test(str2)); // false

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

const reg = /abc/g;
const str1 = 'abcd';
const str2 = 'abcdabcd';

console.log(reg.lastIndex);  // 0
console.log(reg.test(str1));  // true
console.log(reg.lastIndex);  // 3
console.log(reg.test(str1));  // false

console.log(reg.lastIndex);  // 0
console.log(reg.test(str2));  // true
console.log(reg.lastIndex);  // 3
console.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));  // ABCDa
console.log(str.replaceAll(pattern, replacement));  // ABCDA
  • 注意事项 :如果给函数传递的pattern 参数是个正则表达式的话,这个正则表达式必须设置了 g 标识符,不然会抛出一个谬误。
const pattern = /a/;
const replacement = 'A';
const str = 'aBCDa';
console.log(str.replace(pattern, replacement));  // ABCDa
console.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));  // 0
console.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"]

    总结

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

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

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

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

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

    退出移动版