防止在JavaScript中污染RegExp构造函数的属性

15

这有点棘手,我有一个修复的想法,但我想知道是否有(更)简单的方法。

简而言之,在JavaScript中执行正则表达式时,某些属性会在RegExp构造函数上分配值。例如:

/foo/.test('football')
//-> true

RegExp.input
//-> "football"

RegExp.rightContext
//-> "tball"

我希望能够执行一个正则表达式而不影响这些属性。如果不可能(我认为这是不可能的),那么我至少希望在执行后恢复它们之前的值。

我知道 input/$_ 是可写的,但其他大部分属性似乎都不是。一种选择可能是重构一个正则表达式,重新应用所有这些值,但我认为这将非常困难。

我想要这样做的原因是因为我正在编写一个本地API的模拟器,并使用test262套件进行测试。test262套件在某些检查RegExp对象是否具有意外属性值的测试中失败了。


这必须是临时的吗? - Qantas 94 Heavy
4
我喜欢你揭示的难题。然而,我的想法是你正在改变一个不需要为了适应一个损坏的部分而被改变的系统的一部分:这个损坏的部分是你的测试框架。如果你的最终产品不需要RegExp对象保留其原始状态,那么没有理由让测试框架中的任意一个测试强制你重写代码。我会说你想要改变的部分是测试框架。我假设你查找了配置选项等来忽略某些对于RegExp对象的测试? - Hurricane Hamilton
@Hurricane:是的,我本来可以选择忽略它的,而且我几乎也这么做了。但是,如果解决方案不是那么简单,我可能就不会这么做了。然而,由于我正在为本地API编写一个shim,我希望它尽可能接近实现以涵盖所有基础知识。然而,在这种情况下不可能做到这一点,我已经在我的利益方面弯曲了测试规则几次。 - Andy E
2个回答

1
你可以尝试创建一个测试的包装函数:
var fTest = RegExp.test;
RegExp.test = function() {
    var bReturn = fTest.apply(RegExp, arguments);
    delete RegExp.input;
    delete RegExp.rightContext;
    return bReturn;
}

这并不会“恢复”先前的状态(它只是从对象中删除那些属性),而且在大多数属性都是getter的Google Chrome中无法工作。此外,它会改变内置的RegExp.prototype.test方法的行为,这是不期望的。 - Andy E

1
这是最终结果。它比我的初步尝试更加稳健;它正确转义子表达式,确保它们按正确顺序出现,并且不会在找到空表达式时停止:
/**
 * Constructs a regular expression to restore tainted RegExp properties
 */
function createRegExpRestore () {
    var lm  = RegExp.lastMatch,
        ret = {
           input: RegExp.input
        },
        esc = /[.?*+^$[\]\\(){}|-]/g,
        reg = [],
        cap = {};

    // Create a snapshot of all the 'captured' properties
    for (var i = 1; i <= 9; i++)
        cap['$'+i] = RegExp['$'+i];

    // Escape any special characters in the lastMatch string
    lm = lm.replace(esc, '\\$0');

    // Now, iterate over the captured snapshot
    for (var i = 1; i <= 9; i++) {
        var m = cap['$'+i];

        // If it's empty, add an empty capturing group
        if (!m)
            lm = '()' + lm;

        // Else find the escaped string in lm wrap it to capture it
        else
            lm = lm.replace(m.replace(esc, '\\$0'), '($0)');

        // Push to `reg` and chop `lm`
        reg.push(lm.slice(0, lm.indexOf('(') + 1));
        lm = lm.slice(lm.indexOf('(') + 1);
    }

    // Create the property-reconstructing regular expression
    ret.exp = RegExp(reg.join('') + lm, RegExp.multiline ? 'm' : '');

    return ret;
}

它能够完成我最初认为很困难的任务。如果您按以下方式使用它,它应该会将所有属性恢复到其原始值:
var 
    // Create a 'restore point' for RegExp
    old  = createRegExpRestore(),

    // Run your own regular expression
    test = someOtherRegEx.test(someValue);

// Restore the previous values by running the RegExp
old.exp.test(old.input);

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