文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2024-12-24 16:27

关注

在之前的一系列文章中,我们讲解了很多关于正则表达式的知识。那么作为一个前端工程师,如果想要把这些知识应用到我们平时的开发中去的话,就需要知道在JavaScript中,能够使用正则的函数有哪些?然后它们各自的功能是什么?有哪些需要注意的地方?只有掌握好了每一个方法的使用场景,我们才可能在需要使用的时候能够很快的想起来使用哪个方法效率最高,效果最好。

这些确实是一些基础的知识,但是我相信应该有很多同学还没有系统的把这些知识学习一边。相信我,如果你能够把这篇文章看完的话,你肯定可以学习到一些新的知识。知道每一个方法的用途,使用场景,学会在合适的场景选择合适的方法。当然你还能够掌握这些方法需要注意的地方,以防在以后使用的时候陷入了困境。

文章中的代码示例如果没有特别说明的话,都是在Chrome浏览器中进行的。本篇文章的内容比较长,建议先收藏起来,可以以后慢慢细看。

在JavaScript中,能够使用正则表达式的函数有(排除了过时的方法):

RegExp.prototype

首先我们要讲解的是RegExp对象上的两个方法

RegExp.prototype.test()

简单的匹配,根据匹配的结果确定是否匹配成功。 

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

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

  1. const reg = /abc/g;  
  2. const str1 = 'abcd' 
  3. const str2 = 'abcdabcd' 
  4. console.log(reg.lastIndex);  // 0  
  5. console.log(reg.test(str1));  // true  
  6. console.log(reg.lastIndex);  // 3  
  7. console.log(reg.test(str1));  // false  
  8. console.log(reg.lastIndex);  // 0  
  9. console.log(reg.test(str2));  // true  
  10. console.log(reg.lastIndex);  // 3  
  11. console.log(reg.test(str2));  // true 

上面的例子很好地说明了这种情况,如果我们设置了全局标识符g的话,只要我们当前的匹配是成功的,那么接下来如果再次使用同样的正则进行匹配的话就可能会出现问题,因为上一个成功的匹配导致正则表达式对象的lastIndex属性的值发生了变化,那么下次进行匹配的时候是从lastIndex位置开始的,所以就可能会出现一些问题。

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

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

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

在平时的开发中,如果需要判断页面所处的宿主环境的话,我们也会使用test方法去判断当前页面所处的环境。例如,你需要判断当前页面所处的环境是不是iPhone的话,你可能会写出这样的判断: 

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

RegExp.prototype.exec()

需要注意的是,如果没有符合的匹配,返回的结果是null,而不是一个空数组[]。所以当我们需要判断是否有匹配的结果的时候,不能凭感觉觉得返回的值是一个空的数组[]。 

  1. const reg1 = /(\d{2}):(\d{2}):(\d{2})/;  
  2. const str1 = 'Sat Aug 22 2020 17:31:55 GMT+0800 (中国标准时间)' 
  3. const str2 = 'Sat Aug 22 2020' 
  4. 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]  
  5. console.log(reg1.exec(str2));  // null 

从上面的代码中我们可以看到,如果没有匹配结果的话,返回的结果是null。如果能够匹配成功的话,返回的结果是一个数组。在这个结果数组中,第0项表示正则表达式匹配的内容。其中第1..n项表示的是正则表达式中括号的捕获内容,对于上面的示例来说,第1..3项表示的是捕获时间的时分秒。数组还有额外的属性index和input,其中index表示正则表达式匹配到的字符串在原字符串中的位置。input表示原始待匹配的字符串。

  1. const reg = /\d/g;  
  2.     const str = '654321' 
  3.     let result;  
  4.     while ((result = reg.exec(str))) {  
  5.       console.log(  
  6.         `本次匹配到的数字是:${result[0]}, 正则表达式的 lastIndex 的值是:${  
  7.           reg.lastIndex  
  8.         }` 
  9.       );  
  10.     } 

输出的结果如下: 

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

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

当然还有很多的场景可以使用exec方法去处理的,大家在平时的开发中有没有使用过exec方法处理一些问题呢?可以在下面留言,我们大家一起讨论一下,加深一下对这个方法的理解。

String.prototype

接下来我们来讲解一下String.prototype上面有关正则的一些方法。

String.prototype.match()

  1. const reg = /\d/;  
  2. const str = 'abc123' 
  3. console.log(str.match(reg));  // ["1", index: 3, input: "abc123", groups: undefined] 

              1.   没有匹配到结果的返回结果是null。   

  1. const reg = /\d/;  
  2.    const str = 'abc' 
  3.    console.log(str.match(reg));  // null 

              2.  是否设置了g标识符,如果没有设置g的话,match的返回结果跟对应的exec的返回结果是一样的。如果设置了g标识符的话,返回的结果是与正则表达式相匹配的结果的集合。 

  1. const reg = /\d/g;  
  2. const str = 'abc123' 
  3. console.log(str.match(reg));  // ["1", "2", "3"] 

             3.   如果match方法没有传递参数的话,返回的结果是[''],一个包含空字符串的数组。 

  1. const str = 'abc123' 
  2.   console.log(str.match());  // ["", index: 0, input: "abc123", groups: undefined] 

             4.  如果match方法传递的参数是一个字符串或者数字的话,会在内部隐式调用new RegExp(regex),将传入的参数转变为一个正则表达式。 

  1. const str = 'abc123' 
  2. console.log(str.match('b'));  // ["b", index: 1, input: "abc123", groups: undefined] 

简单获取url中的查询参数:   

  1. const query = {};  
  2.    // 首先使用带有g标识符的正则,表示全局查找  
  3.    const kv = location.search.match(/\w*=\w*/g);  
  4.    if (kv) {  
  5.      kv.forEach(v => {  
  6.          // 使用不带g标识符的正则,需要获取括号中的捕获内容  
  7.        const q = v.match(/(\w*)=(\w*)/);  
  8.        query[q[1]] = q[2];  
  9.      });  
  10.    } 

String.prototype.matchAll()

  1. const reg = /(\w*)=(\w*)/g;  
  2. const str = 'a=1,b=2,c=3' 
  3. console.log([...str.matchAll(reg)]); 

String.prototype.matchAll()

                1.  跟match方法相同的地方是,如果传递给matchAll方法的参数不是一个正则表达式的话,那么会隐式调用new RegExp(obj)将其转换为一个正则表达式对象。 传递给matchAll的正则表达式需要是设置了g标识符的,如果没有设置g标识符,那么就会抛出一个错误。   

  1. const reg = /(\w*)=(\w*)/;  
  2.     const str = 'a=1,b=2,c=3' 
  3.     console.log([...str.matchAll(reg)]);  // Uncaught TypeError: String.prototype.matchAll called with a non-global RegExp argument 

               2.  在可以使用matchAll的情况下,使用matchAll比使用exec方法更便捷一些。因为在全局需要匹配的情况下,使用exec方法需要配合循环来使用,但是使用matchAll就可以不使用循环。

               3.  matchAll方法在字符串执行匹配的过程中,正则表达式的lastIndex属性不会更新。更多详情可以参考String.prototype.matchAll()

还是以上面的获取url中的查询参数这个小功能来实践一下: 

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

String.prototype.replace()

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

          1.  pattern是字符串,replacement也是字符串。这种形式在平时的开发中使用的比较多。   

  1. const pattern = 'a' 
  2.    const replacement = 'A' 
  3.    const str = 'aBCD' 
  4.    console.log(str.replace(pattern, replacement));  // ABCD 

          2.  pattern是正则表达式,replacement是字符串。 

  1. const pattern = /__(\d)__/;  
  2.  const replacement = "--$$--$&--$`--$'--$1--" 
  3.  const str = 'aaa__1__bbb' 
  4.  console.log(str.replace(pattern, replacement));  // aaa--$--__1__--aaa--bbb--1--bbb 

如果replacement是字符串,那么在这个字符串中可以使用一些特殊的变量,具体可参考Specifying a string as a parameter。

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

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

  1. 匹配到的字符串是:__1__  
  2. 捕获到的内容是:1  
  3. 匹配的位置是:3  
  4. 原始待匹配的字符串是:aaa__1__bbb  
  5. 命名的捕获内容是:{"number":"1"} 

如果你对replacement是函数这种情况不是很了解的话可以看看Specifying a function as a parameter,里面会有详细的解释,这里就不在具体解释了。

          需要注意的地方就是当我们的pattern是正则表达式的时候,要注意是否设置了g标识符,因为如果没有设置g标识符的话,只会进行一次匹配。设置了g标识符的话,会进行全局的匹配。

          对于前端来说,对用户的输入进行校验时很常见的需求。假如我们有一个输入框,只允许用户输入数字,我们可以这样处理: 

  1. const reg = /\D/g;  
  2.  const str = 'abc123' 
  3.  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浏览器上进行试验。 

  1. const pattern = 'a' 
  2. const replacement = 'A' 
  3. const str = 'aBCDa' 
  4. console.log(str.replace(pattern, replacement));  // ABCDa  
  5. console.log(str.replaceAll(pattern, replacement));  // ABCDA 
  1. const pattern = /a/;  
  2. const replacement = 'A' 
  3. const str = 'aBCDa' 
  4. console.log(str.replace(pattern, replacement));  // ABCDa  
  5. console.log(str.replaceAll(pattern, replacement));  // Uncaught TypeError: replaceAll must be called with a global RegExp 

String.prototype.search()

  1. const reg = /\d/;  
  2. const str1 = '123' 
  3. const str2 = 'abc' 
  4. console.log(str1.search(reg));  // 0  
  5. console.log(str2.search(reg));  // -1 

如果你需要找到特定匹配在字符串中的位置的话,那么可以使用search方法。 

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

String.prototype.split()

                1.  分割器separator是字符串: 

  1. const str = 'hello, world!' 
  2.  console.log(str.split(''));  // ["h", "e", "l", "l", "o", ",", " ", "w", "o", "r", "l", "d", "!"] 

                2.  分割器separator是正则表达式: 

  1. const str = 'abc1abc2abc3' 
  2. const separator = /\w(?=\d)/;  
  3. console.log(str.split(separator));  // ["ab", "1ab", "2ab", "3"] 

               1.  如果split方法没有传递参数的话,会返回一个包含原字符串的数组: 

  1. const str = 'hello, world!' 
  2.  console.log(str.split());  // ["hello, world!"] 

             2.  因为JavaScript的字符串是使用UTF-16进行编码的,该编码使用一个16比特的编码单元来表示大部分常见的字符,使用两个编码单元表示不常用的字符。所以对于一些不常用的字符来说,在使用split方法进行字符串分割的时候可能会出现一些问题: 

  1. const str = '😀😃😄😁😆😅' 
  2.  console.log(str.split(''));  // ["�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�"] 

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

  1. const str = '😀😃😄😁😆😅' 
  2. console.log([...str]);  // ["😀", "😃", "😄", "😁", "😆", "😅"] 

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

  1. const str = '😀😃😄😁😆😅' 
  2. const separator = /(?=[\s\S])/u;  
  3. console.log(str.split(separator)); // ["😀", "😃", "😄", "😁", "😆", "😅"] 
  1. const str = 'abc1abc2abc3' 
  2. const separator = /(\w)(?=\d)/;  
  3. console.log(str.split(separator));  // ["ab", "c", "1ab", "c", "2ab", "c", "3"] 
  1. const str = 'hello, world!' 
  2. console.log(str.split('', 3));  // ["h", "e", "l"] 

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

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

总结

当我们能够把上面的这些方法都熟练的掌握之后,那么在实际的开发中再结合正则表达式来使用的话,那简直就是如虎添翼,能够在一些场景下提高我们开发的效率。

当然光靠看看文章是不能够很好地将这些知识点都记牢固的,你需要的是一个一个的实践一下,这样才能够加深自己的记忆,才能够记得更牢固。

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

如果你对本篇文章有什么意见和建议,都可以直接在文章下面留言,也可以在这里提出来。也欢迎大家关注我的公众号关山不难越,学习更多实用的前端知识,让我们一起努力进步吧。 

 

来源:segmentfault内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯