zoukankan      html  css  js  c++  java
  • JS 正则中的命名捕获分组

    假设你在一段陌生的代码中看到这样一个函数: 

    function toLocalDate(date) {
      return date.replace(/(d{2})-(d{2})-(d{4})/, "$2-$1-$3")
    }

    单看这个函数你能知道它是想把“日-月-年”替换成“月-日-年”,还是反过来?匿名捕获分组没法做到这一点,那就该命名捕获分组上场了:

    function toLocalDate(date){
      return date.replace(/(?<month>d{2})-(?<day>d{2})-(?<year>d{4})/, "$<day>-$<month>-$<year>")
    }

    俗话说的好,“一个好的变量名赛过一行注释”,命名捕获分组很大的一个作用就是它能起到注释的作用。

    另外,命名捕获分组还有一个好处,那就是假如你在修改一个正则时,在已有分组的左边引入了新的分组,那么你还得记得更新已有的反向引用的数字。比如将 (foo)1 改成了 (bar)(foo)1,那你得把原来的 1 改成 2,replace() 方法的第二个参数里的 $1 也同样得改,用命名分组不会有这个问题。

    语法

    命名捕获分组自身的语法是 (?<name>...),比普通的分组多了一个 ?<name> 字样,其中 name 的起法就和你平时起变量名一样即可(不过在这里关键字也可用)。

    反向引用一个命名分组的语法是 k<name>,注意命名分组同样可以通过数字索引来反向引用,比如:

    /(?<foo>a)k<foo>1/.test("aaa") // true

    在 replace() 方法的替换字符串中反向引用是用 $<name>:

    "abc".replace(/(?<foo>a)/, "$<foo>-") // "a-bc",同样 $1 仍然可用

    总结一下就是,和命名分组相关的有三种语法,分别是 ?<name>、k<name>、$<name>,相同点是都用尖括号包裹着分组名。

    在 API 中的使用

    在 exec() 和 match() 中的使用:

    const groups = "04-25-2017".match(/(?<month>d{2})-(?<day>d{2})-(?<year>d{4})/).groups // {month: "04", day: "25", year: "2017"}
    
    const {day, month, year} = groups

    exec() 和 match() 方法返回的匹配结果数组上多了一个 groups 属性,里面存放着每个命名分组的名称以及它们匹配到的值,利用 ES6 的解构语法,可以方便的提取出想要的字段。注意这个 groups 属性只有在当前正则里至少存在一个命名分组的前提下才会存在,比如:

    /(d{2})-(d{2})-(d{4})/.exec("04-25-2017").groups // undefined,因为没有命名分组

    在 replace(/.../, replacement) 中的使用:

    replacement 是字符串的情况上面已经举过例子了,这里主要讲它是函数的情况:

    "04-25-2017".replace(/(?<month>d{2})-(?<day>d{2})-(?<year>d{4})/, (...args) => {
      const groups = args.slice(-1)[0]
      const {day, month, year} = groups
      return `${day}-${month}-${year}`
    }) // "25-04-2017"

    也就是说,在实参列表的最末尾,多传了一个 groups 对象。同样,如果正则里没有命名分组,这个参数不会存在。

    异常情况

    分组名不能有重复项:

    /(?<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>",不包含命名分组时会向后兼容

    总结

    V8 目前已经完全实现了命名捕获分组的提案 https://tc39.github.io/proposal-regexp-named-groups/

    命名分组虽然带来了一些好处,但我个人觉得,正则越长越难读懂,尤其增加的长度是一堆小括号和尖括号。在可读性上,命名分组也许会起到反作用,尤其对正则苦手来说。 

  • 相关阅读:
    Json对象与Json字符串互转(4种转换方式)
    Web.config配置文件详解
    jQuery BlockUI Plugin Demo 6(Options)
    jQuery BlockUI Plugin Demo 5(Simple Modal Dialog Example)
    jQuery BlockUI Plugin Demo 4(Element Blocking Examples)
    jQuery BlockUI Plugin Demo 3(Page Blocking Examples)
    jQuery BlockUI Plugin Demo 2
    <configSections> 位置引起的错误
    关于jQuery的cookies插件2.2.0版设置过期时间的说明
    jQuery插件—获取URL参数
  • 原文地址:https://www.cnblogs.com/ziyunfei/p/6761413.html
Copyright © 2011-2022 走看看