JavaScript 正则命名分组【推荐】


Posted in Javascript onJune 07, 2018

前言

以往我们只是习惯于通过数组下标来访问正则匹配到的分组,但分组达到4、5个时,标识起来就会非常麻烦。V8早已实现了正则命名分组提案,只是我们很少使用,本文将介绍JS的正则命名分组。

以往的做法

假设要使用正则匹配一个日期的年月日,以往我们会这样做:

const RE_DATE = /(\d{4})-(\d{2})-(\d{2})/;
const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj[1]; // 1999
const month = matchObj[2]; // 12
const day = matchObj[3]; // 31

这里有几个缺点:

  • 要找到一个分组的位置,你必须要去数括号的位置,有时嵌套起来会更令人头疼。
  • 后面维护代码的同学阅读起来,还要根据下标找到正则里面对应的括号,并且要再次阅读括号里面的正则才知道含义。
  • 当你调整正则捕获分组的数量、顺序或嵌套时,你必要还要对下面的代码做调整。

所有这些问题,都可以通过正则命名分组来解决。

现在的玩法

现在你只需要给分组里面一个命名标识即可:

(?<year>\d{4})

这里,我们用变量year标记了上一个捕获组#1。 该名称必须是合法的JavaScript标识符。 匹配后,您可以通过matchObj.groups.year访问捕获的字符串。

让我们通过命名分组重写前面的代码:

const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj.groups.year; // 1999
const month = matchObj.groups.month; // 12
const day = matchObj.groups.day; // 31

如果正则里面有了命名分组,那么匹配结果会多了一个groups 的属性,这个属性中包含了一切命名分组的捕获结果。配合上解构大法使用又是一股清流:

const {groups: {day, year}} = RE_DATE.exec('1999-12-31');
console.log(year); // 1999
console.log(day); // 31

当然,即使你使用了命名分组,那么返回的结果还可以通过以往的数组下标方式访问:

const year2 = matchObj[1]; // 1999
const month2 = matchObj[2]; // 12
const day2 = matchObj[3]; // 31

命名分组具有以下优点:

  • 找到分组的“ID”更容易。
  • 匹配的代码变得自描述性,因为分组的ID描述了捕获的内容。
  • 如果更改分组的顺序,则不必更改匹配的代码。
  • 分组的名称也使正则表达式更易于理解,因为您可以直接看到每个组的用途。

反向引用

反向引用命名分组\k<name>

看下面这个匹配重复单词的例子:

const RE_TWICE = /^(?<word>[a-z]+)!\k<word>$/;
RE_TWICE.test('abc!abc'); // true
RE_TWICE.test('abc!ab'); // false

同时也可以使用以往的反向引用方式:

const RE_TWICE = /^(?<word>[a-z]+)!\1$/;
RE_TWICE.test('abc!abc'); // true
RE_TWICE.test('abc!ab'); // false

replace( )

字符串方法replace()以两种方式支持命名分组:

方式一

const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
console.log('1999-12-31'.replace(RE_DATE,
 '$<month>/$<day>/$<year>'));
 // 12/31/1999

如果replace不一定是直接返回新的拼接字符串,那么可以看看下面的办法:

方式二

const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
console.log('1999-12-31'.replace(
 RE_DATE,
 (g, y, m, d, offset, input, {year, month, day}) =>
  month+'/'+day+'/'+year));
 // 12/31/1999

看看这replace的callback形参密密麻麻看得心慌慌,很多都用不上,那么我们看看更简单的写法:

console.log('1999-12-31'.replace(RE_DATE,
 (...args) => {
  const {year, month, day} = args.slice(-1)[0];
  return month+'/'+day+'/'+year;
 }));
 // 12/31/1999

这里配合上spread operator直取最后一个参数,再接上一个解构大法,结果又是一股清流。

命名分组没有匹配结果?

如果可选的命名组不被匹配,则其属性值被设置为undefined,但key是仍存在:

const RE_OPT_A = /^(?<as>a+)?$/;
const matchObj = RE_OPT_A.exec('');
// We have a match:
console.log(matchObj[0] === ''); // true
// Group <as> didn't match anything:
console.log(matchObj.groups.as === undefined); // true
// But property as exists:
console.log('as' in matchObj.groups); // true

异常情况

分组名不能有重复项:

/(?<foo>a)(?<foo>b)/ // SyntaxError: Duplicate capture group name

反向引用一个不存在的分组名:

/\k<foo>/u // SyntaxError: Invalid named capture referenced
/\k<foo>/.test("k<foo>") // true, 非 Unicode 下为了向后兼容,k 前面的 \ 会被丢弃

在 reaplce() 方法的替换字符串中引用一个不存在的分组:

"abc".replace(/(?<foo>.*)/, "$<bar>") // SyntaxError: Invalid replacement string
"abc".replace(/(.*)/, "$<bar>") // "$<bar>",不包含命名分组时会向后兼容

最后

  • Chrome60 已支持命名分组
  • 通过babel插件处理兼容问题

babel-plugin-transform-modern-regexp

总结

以上所述是小编给大家介绍的JavaScript 正则命名分组,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
jQuery 1.3 和 Validation 验证插件1.5.1
Jul 09 Javascript
两种简单实现菜单高亮显示的JS类代码
Jun 27 Javascript
jQuery的cookie插件实现保存用户登陆信息
Apr 15 Javascript
Vue.js基础知识汇总
Apr 27 Javascript
简单分析javascript中的函数
Sep 10 Javascript
JS查找字符串中出现最多的字符及个数统计
Feb 04 Javascript
React-Native中props具体使用详解
Sep 04 Javascript
JS+canvas绘制的动态机械表动画效果
Sep 12 Javascript
使用原生js编写一个简单的框选功能方法
May 13 Javascript
简单谈谈javascript高级特性
Sep 04 Javascript
vue-simple-uploader上传成功之后的response获取代码
Sep 07 Javascript
vue二维数组循环嵌套方式 循环数组、循环嵌套数组
Apr 24 Vue.js
详解create-react-app 自定义 eslint 配置
Jun 07 #Javascript
vue.js实现标签页切换效果
Jun 07 #Javascript
js数组去重的N种方法(小结)
Jun 07 #Javascript
vue+axios新手实践实现登陆的示例代码
Jun 06 #Javascript
vue2.0实现音乐/视频播放进度条组件
Jun 06 #Javascript
vue实现简单loading进度条
Jun 06 #Javascript
security.js实现的RSA加密功能示例
Jun 06 #Javascript
You might like
php数据入库前清理 注意php intval与mysql的int取值范围不同
2010/12/12 PHP
php构造函数实例讲解
2013/11/13 PHP
PHP写的资源下载防盗链类分享
2014/05/12 PHP
PHP获取文件行数的方法
2015/06/10 PHP
Linux系统递归生成目录中文件的md5的方法
2015/06/29 PHP
浅析PHP类的反射来实现依赖注入过程
2018/02/06 PHP
jQuery中的bind绑定事件与文本框改变事件的临时解决方法
2010/08/13 Javascript
Javascript 实现复制(Copy)动作方法大全
2014/06/20 Javascript
关闭页面window.location事件未执行的原因及解决方法
2014/09/01 Javascript
Jquery中find与each方法用法实例
2015/02/04 Javascript
jQuery mobile转换url地址及获取url中目录部分的方法
2015/12/04 Javascript
js前端日历控件(悬浮、拖拽、自由变形)
2017/03/02 Javascript
js实现手机发送验证码功能
2017/03/13 Javascript
如何使用Bootstrap 按钮实例详解
2017/03/29 Javascript
微信小程序 侧滑删除(左滑删除)
2017/05/23 Javascript
微信小程序授权获取用户详细信息openid的实例详解
2017/09/20 Javascript
微信小程序switch开关选择器使用详解
2018/01/31 Javascript
浅谈vue项目可以从哪些方面进行优化
2018/05/05 Javascript
Vue使用vue-draggable 插件在不同列表之间拖拽功能
2020/03/12 Javascript
Paypal支付不完全指北
2020/06/04 Javascript
整理Python 常用string函数(收藏)
2016/05/30 Python
Python使用wxPython实现计算器
2018/01/30 Python
使用PyInstaller将python转成可执行文件exe笔记
2018/05/26 Python
python元组和字典的内建函数实例详解
2019/10/22 Python
关于numpy中eye和identity的区别详解
2019/11/29 Python
python时间与Unix时间戳相互转换方法详解
2020/02/13 Python
html5指南-5.使用web storage存储键值对的数据
2013/01/07 HTML / CSS
sleep()方法和wait()方法的区别是什么
2012/11/17 面试题
如何理解transaction事务的概念
2015/05/27 面试题
《跟踪台风的卫星》教学反思
2014/04/10 职场文书
医生个人自我剖析材料
2014/10/08 职场文书
Web前端:CSS最强总结 附详细代码
2021/03/31 HTML / CSS
NodeJs内存占用过高的排查实战记录
2021/05/10 NodeJs
SQL实现LeetCode(180.连续的数字)
2021/08/04 MySQL
MySQL中一条update语句是如何执行的
2022/03/16 MySQL
详解Python flask的前后端交互
2022/03/31 Python