关于正则编写的细节这里不多做赘述,本文主要是列出 execmatchmatchAlltest 这几个函数之间的区别。

详细的正则表达式文档参照 正则表达式 - JavaScript | MDN


exec 函数

  • 调用者:正则表达式

  • 返回示例

    属性 描述
    [0][1][2] 下标 0 对应的是最近一个匹配到的字符串,往后的下标对应捕获的子串(也就是正则表达式里用括号包裹的内容)
    index 此次匹配到的字符串在原始字符串中的索引值
    input 原始字符串
  • 当正则表达式携带 g 标志时,exec 函数会从正则表达式变量的 lastIndex(默认为 0)起开始检索原始字符串,一旦匹配成功就会停止继续向后匹配,并会在执行后把正则表达式变量的 lastIndex 值置成此次匹配的子串末尾的下标 + 1(如果匹配成功),或置回 0(如果匹配失败)。这就会导致多次执行 exec 可能会有不同的结果,像下面这样:

    let myRe = /d(b+)(c*)d/g;
    let str = 'cdbbcdbsbzdbd';
    
    myRe.exec(str);
    // 第 1 次执行结果 => ["dbbcd", "bb", "c", index: 1, input: "cdbbcdbsbzdbd"]
    // 上面结果中的 "bb" 就是第一个括号捕获的内容,"c" 就是第二个括号捕获的内容
    // 执行完后 myRe.lastIndex 为 6,下一次 exec 将从原始字符串下标 6 开始检索
    
    myRe.exec(str);
    // 第 2 次执行结果 => ["dbd", "b", "", index: 10, input: "cdbbcdbsbzdbd"]
    // 上面结果中的 "b" 就是第一个括号捕获的内容,"" 就是第二个括号捕获的内容
    // 执行完后 myRe.lastIndex 为 13,下一次 exec 将从原始字符串下标 13 开始检索
    
    myRe.exec(str);
    // 第 3 次执行 => null
    // 执行完后 myRe.lastIndex 为 0,下一次 exec 将从原始字符串下标 0 开始检索
    
    myRe.exec(str);
    // 第 4 次执行结果 => ["dbbcd", "bb", "c", index: 1, input: "cdbbcdbsbzdbd"]
    // 执行完后 myRe.lastIndex 为 6
    
  • 不过正如上面所说,lastIndex 是正则表达式变量的一个属性,如果你没有把正则表达式赋给一个变量,每次用的都是新的正则表达式,那就不存在多次执行导致不同结果的现象了,像下面这样:

    let str = 'cdbbcdbsbzdbd';
    
    /d(b+)(c*)d/g.exec(str);
    // 执行结果 => ["dbbcd", "bb", "c", index: 1, input: "cdbbcdbsbzdbd"]
    
    /d(b+)(c*)d/g.exec(str);
    // 执行结果 => ["dbbcd", "bb", "c", index: 1, input: "cdbbcdbsbzdbd"]
    
    /d(b+)(c*)d/g.exec(str);
    // 执行结果 => ["dbbcd", "bb", "c", index: 1, input: "cdbbcdbsbzdbd"]
    
  • 当正则表达式 携带 g 标志时,exec 函数同样一旦匹配成功就会停止继续向后匹配,并且不会改变正则表达式变量的 lastIndex,这样每次调用 exec 得到的结果都是相同的,像下面这样:

    let myRe = /d(b+)(c*)d/;
    let str = 'cdbbcdbsbzdbd';
    
    myRe.exec(str);
    // 第 1 次执行结果 => ["dbbcd", "bb", "c", index: 1, input: "cdbbcdbsbzdbd"]
    // 执行完后 myRe.lastIndex 为 0,下一次 exec 将从原始字符串下标 0 开始检索
    
    myRe.exec(str);
    // 第 2 次执行结果 => ["dbbcd", "bb", "c", index: 1, input: "cdbbcdbsbzdbd"]
    // 执行完后 myRe.lastIndex 为 0,下一次 exec 将从原始字符串下标 0 开始检索
    
    myRe.exec(str);
    // 第 3 次执行 => ["dbbcd", "bb", "c", index: 1, input: "cdbbcdbsbzdbd"]
    // 执行完后 myRe.lastIndex 为 0,下一次 exec 将从原始字符串下标 0 开始检索
    

match 函数

  • 调用者:字符串

  • 当正则表达式携带 g 标志时,match 函数会以数组形式返回所有匹配的子串(并不会捕获括号中的内容),不会更改正则表达式变量的 lastIndex,像下面这样:

    let myRe = /d(b+)(c*)d/g;
    let str = 'cdbbcdbsbzdbd';
    
    str.match(myRe);
    // 执行结果 => ["dbbcd", "dbd"]
    // 执行完后 myRe.lastIndex 为 0
    
  • 当正则表达式 携带 g 标志时,match 函数的返回形式与 exec 函数一致,并且不会改变正则表达式变量的 lastIndex,像下面这样:

    let myRe = /d(b+)(c*)d/;
    let str = 'cdbbcdbsbzdbd';
    
    str.match(myRe);
    // 执行结果 => ["dbbcd", "bb", "c", index: 1, input: "cdbbcdbsbzdbd"]
    // 执行完后 myRe.lastIndex 为 0
    

matchAll 函数

  • 调用者:字符串

  • matchAll 使用的正则表达式必须携带 g 标志,matchAll 函数会以迭代器(iterator)形式返回所有匹配的子串,每个子串都是与 exec 函数的返回格式一致,不会更改正则表达式变量的 lastIndex,像下面这样:

    let myRe = /d(b+)(c*)d/g;
    let str = 'cdbbcdbsbzdbd';
    
    [...str.matchAll(myRe)];
    // 执行结果 => 
    // [
    //     ["dbbcd", "bb", "c", index: 1, input: "cdbbcdbsbzdbd"],
    //     ["dbd", "b", "", index: 10, input: "cdbbcdbsbzdbd"]
    // ]
    // 执行完后 myRe.lastIndex 为 0
    

test 函数

  • 调用者:正则表达式

  • 返回布尔值,true 匹配成功,false 代表匹配失败

  • test 函数与 exec 函数在对待 lastIndex 上的行为完全一样,在携带 g 标志时,从正则表达式变量的 lastIndex(默认为 0)起开始检索原始字符串,一旦匹配成功就会停止继续向后匹配,并会在执行后把正则表达式变量的 lastIndex 值置成此次匹配的子串末尾的下标 + 1(如果匹配成功),或置回 0(如果匹配失败);不携带 g 标志时,test 函数同样一旦匹配成功就会停止继续向后匹配,并且不会改变正则表达式变量的 lastIndex

    let myRe = /d(b+)(c*)d/g;
    let str = 'cdbbcdbsbzdbd';
    
    myRe.test(str);
    // 第 1 次执行结果 => true
    // 执行完后 myRe.lastIndex 为 6,下一次 test 将从原始字符串下标 6 开始检索
    
    myRe.test(str);
    // 第 2 次执行结果 => true
    // 执行完后 myRe.lastIndex 为 13,下一次 test 将从原始字符串下标 13 开始检索
    
    myRe.test(str);
    // 第 3 次执行 => false
    // 执行完后 myRe.lastIndex 为 0,下一次 test 将从原始字符串下标 0 开始检索
    
    myRe.test(str);
    // 第 4 次执行结果 => true
    // 执行完后 myRe.lastIndex 为 6
    
    
    let myRe2 = /d(b+)(c*)d/;  // myRe2 不携带 g 标志
    
    myRe2.test(str);
    // 第 1 次执行结果 => true
    // 执行完后 myRe.lastIndex 为 0,下一次 test 将从原始字符串下标 0 开始检索
    
    myRe2.test(str);
    // 第 2 次执行结果 => true
    // 执行完后 myRe.lastIndex 为 0,下一次 test 将从原始字符串下标 0 开始检索
    
    myRe2.test(str);
    // 第 3 次执行 => true
    // 执行完后 myRe.lastIndex 为 0,下一次 test 将从原始字符串下标 0 开始检索