更改RegExp标志

时间:2011-04-29 17:47:21

标签: javascript regex prototype flags

所以基本上我自己编写了这个函数,以便能够计算字符串中子字符串的出现次数:

String.prototype.numberOf = function(needle) {
  var num = 0,
      lastIndex = 0;
  if(typeof needle === "string" || needle instanceof String) {
    while((lastIndex = this.indexOf(needle, lastIndex) + 1) > 0)
      {num++;} return num;
  } else if(needle instanceof RegExp) {
    // needle.global = true;
    return this.match(needle).length;
  } return 0;
};

该方法本身表现相当不错,基于RegExp和String的搜索与执行时间相当(在整个庞大的Ray Bradbury的“451 Fahrenheit”上搜索所有“the”的时间都是〜2ms)。 / p> 但是,让我感到困扰的是,无法更改提供的RegExp实例的标志。在没有提供的正则表达式的全局标志设置为true的情况下调用此函数中的 String.prototype.match 是没有意义的,因为它只会记录第一次出现。你当然可以在传递给函数的每个RegExp上手动设置标志,但我更喜欢能够克隆然后操作提供的正则表达式的标志。

令人惊讶的是,我不允许这样做,因为 RegExp.prototype.global 标志(更确切地说是所有标志)似乎是只读的。从那里评论出来的第8行。

所以我的问题是:是否有很好的方法来更改RegExp对象的标志?

我真的不想做这样的事情:

if(!expression.global)
  expression = eval(expression.toString() + "g");

某些实现可能不支持 RegExp.prototype.toString 事件,只是从 Object.prototype 继承它,或者它可能完全是不同的格式。从一开始,这似乎是一个糟糕的编码实践。

4 个答案:

答案 0 :(得分:14)

首先,当needle是不匹配的正则表达式时,您当前的代码无法正常工作。即以下行:

return this.match(needle).length;

当没有匹配时,match方法会返回null。然后,当length的{​​{1}}属性(访问失败)时,会生成JavaScript错误。这很容易解决:

null

现在问题就在眼前。当您说var m = this.match(needle); return m ? m.length : 0; globalignoreCase是只读属性时,您是对的。唯一的选择是创建一个新的RegExp。这很容易完成,因为正则表达式源字符串存储在multiline属性中。以下是您的函数的经过测试的修改版本,它可以纠正上述问题并在re.source尚未设置needle标志时创建新的RegExp对象:

global

答案 1 :(得分:8)

var globalRegex = new RegExp(needle.source, "g");

Live Demo 编辑:m只是为了证明您可以设置多个修饰符

var regex = /find/;
var other = new RegExp(regex.source, "gm");
alert(other.global);
alert(other.multiline);

答案 2 :(得分:4)

您无能为力,但强烈建议您避免使用eval。您可以扩展RegExp原型来帮助您。

RegExp.prototype.flags = function () {
    return (this.ignoreCase ? "i" : "")
        + (this.multiline ? "m" : "")
        + (this.global ? "g" : "");
};

var reg1 = /AAA/i;
var reg2 = new RegExp(reg1.source, reg1.flags() + 'g');

答案 3 :(得分:1)

r = new Regexp(r.source, r.flags + (r.global ? "" : "g"));