意外的Javascript RegExp行为

9

我在JavaScript中创建了一个RegExp对象,用于测试数字的存在:

var test = new RegExp( '[0-9]', 'g' );

我这样使用它。
console.log( test.test( '0' ) ); // true
console.log( test.test( '1' ) ); // false - why?

这个的输出结果更加混乱:
console.log( test.test( '1' ) ); // true
console.log( test.test( '0' ) ); // false - why?
console.log( test.test( '1' ) ); // true
console.log( test.test( '2' ) ); // false - why?
console.log( test.test( '2' ) ); // true - correct, but why is this one true?

如果我删除g限定符,它会按预期工作。这是一个错误吗?还是规范的某个奇怪部分?g限定符应该这样使用吗?(我正在为多个任务重复使用同一表达式,因此必须使用限定符)
3个回答

7

谢谢。你比jfriend00晚了几分钟,但这似乎是一个更完整的答案。只等倒计时结束就会接受! - Dave

6

去掉'g'标志。当你使用'g'标志时,它会更新正则表达式的lastIndex属性(准备在同一字符串上进行后续搜索),然后从该索引值开始下一次搜索(从而使下一次搜索出现错误)。

类似的问题和答案在这里:为什么Regex Javascript // g标志会影响状态?


你的意思是第二次调用它时它会忽略输入字符串,并继续在先前给定的字符串中寻找匹配项吗?这相当奇怪。 - Dave
@Dave。更新正则表达式对象中的lastIndex属性并从那里开始第二次匹配会导致您得到错误的结果。 - jfriend00
啊,好的,我现在明白了。我做了一个快速测试,似乎它使用新字符串,但是从上一次匹配的索引开始搜索:http://jsfiddle.net/SnxSg/ 这一定是有史以来最奇怪的行为,但至少我现在理解了。谢谢! - Dave

1
根据MDN,与exec(或与其结合使用)类似,对同一全局正则表达式实例多次调用test将超过先前的匹配。从技术上讲,ECMAScript 5.1规范说:

15.10.6.3 RegExp.prototype.test(string)

执行以下步骤:

  1. 使用 string 作为参数,在此 RegExp 对象上运行 RegExp.prototype.exec (15.10.6.2) 算法,将结果赋值给变量 match。
  2. 如果 match 不为 null,则返回 true;否则返回 false。

15.10.6.2 RegExp.prototype.exec(string)

对正则表达式与 string 进行匹配,并返回一个包含匹配结果的数组对象,如果 string 没有匹配,则返回 null。

按照以下方式搜索 String ToString(string) 中是否存在正则表达式模式:

  1. R 为当前的 RegExp 对象。
  2. [...]
  3. [...]
  4. 使用参数 "lastIndex" 调用 R 的内部方法 [[Get]] 并将结果赋值给变量 lastIndex
  5. i 为 ToInteger(lastIndex) 的值。
  6. 使用参数 "global" 调用 R 的内部方法 [[Get]] 并将结果赋值给变量 global
  7. 如果 global 为 false,则将 i 设为 0。
  8. [...]
  9. [...]
  10. erendIndex 值。
  11. 如果 global 为 true,
    1. 使用参数 "lastIndex"、e 和 true 调用 R 的内部方法 [[Put]]。
  12. [...]
因此,为了避免这种行为,您可以使用以下方法:
  • 避免使用全局标志g

    这样,在第7步中,i将为0而不是lastIndex

  • 每次使用后手动重置lastIndex

    lastIndex属性的值指定下一个匹配开始的字符串位置。

    例如,

    var test = /[0-9]/g;
    test.test('0');      // true
    test.lastIndex;      // 1
    test.lastIndex = 0;
    test.test('1');      // true
    
  • 使用matchsearch字符串方法

    match会将lastIndex重置为0,而search则忽略它:

    15.5.4.10 String.prototype.match (regexp)

    [...] [如果]globaltrue,则使用参数"lastIndex"和0调用rx的[[Put]]内部方法。[...]

    15.5.4.12 String.prototype.search (regexp)

    [...]从开头搜索值string以查找正则表达式模式rx的出现。[...]执行搜索时,忽略regexplastIndexglobal属性。[...]

    例如,

    var test = /[0-9]/g;
    test.test('0');        // true
    test.lastIndex;        // 1
    '0'.search(test) > -1; // true
    test.lastIndex;        // 1 (未更改)
    !!'0'.match(test);     // true
    test.lastIndex;        // 0
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接