如何在JavaScript中覆盖eval函数?

10
例如:
(function() {
      var proxied = window.eval;
      window.eval = function() {
        return proxied.apply(this, arguments);
      };
    })();

但是这段代码没有起作用。


20
让我想到你这样做的原因有点吓人...... - Paddy
奇怪的是,由于“alert”和其他函数被覆盖了。 - ko1ik
1
@all:他在我下面的回答中评论了他的用例。 - T.J. Crowder
6个回答

13

eval 是魔法般的函数。与“真正的”函数不同,它可以读取和写入调用者的本地变量:

function foo() {
    var a= 1;
    eval('a+= 1');
    alert(a); // 2
}

用代理函数替换eval会出现一个问题:执行a+=1的作用域是proxied函数而不是foo。根据被评估代码中发生的情况,这可能导致值丢失、代理的本地损坏、意外的全局变量等问题。

因此,无法用完全可用的代理替换eval。(对于简单的情况,如果不需要使用局部变量,可能可以尝试使用)


关于“不可能”的问题,可以使用完全可用的代理MyEval来替换eval,但是你需要eval——MyEval(_=>eval(_)) - Pacerier

12

你无法这样做。(虽然有一种 有限的 方法,但非常有限且无法保持bobince 所提到的奇妙效果。)

eval 在至少一个主要实现中不是真正的 JavaScript 函数(IE 的 JScript,至少在 IE7 及之前版本中不是;还没有测试过新的 IE8 版本),因此您将会遇到麻烦,因为您将无法通过 apply 调用原始函数(尽管对于 eval 来说这并不重要)。

最近的 ECMAScript 5 规范在严格模式下明确禁止覆盖 eval(尽管您在那里未使用严格模式),这使我怀疑不覆盖它的理由是非常充分的


3
严格模式禁止别名/覆盖eval,因为解释器无法事先确定函数块中是否包含隐藏的eval使用。如果解释器(尤其是JIT)能够确保函数仅包含简单代码且没有eval,则可以在生成的代码上执行更广泛的优化。 - bobince
1
使用Eval动态构建ExtJs组件。例如: Ext.data.Connection().request({ ... ,success: function(response, opts){ eval(response.responseText); } })组件存储在服务器上。 这在多个项目中使用。在一个项目中,从“eval”返回的值用作一个全局方法的传入参数,而在另一个项目中则不使用。我需要保留此代码以保持功能性。 - ko1ik
@ko1ik:你不会想通过覆盖“eval”来解决这个问题,因为这将是一个维护噩梦。恐怕你必须采用另一种方法,比如更新使用“eval”的部分,改为使用你提供的某个函数,该函数在需要时调用“eval”。 - T.J. Crowder

6

虽然不是可移植的,但以下方法在某些情况下可以起作用,否则可能无法满足ES5的要求:A)它必须以MemberExpression中的Reference形式检索,而不是Value形式;B)它会导致“标准内置函数”。—ES5 #15.1.2)

(function() {
  var proxied = window.eval
  with({get eval(){ console.log('eval called'); return proxied }}) {
    /* client code */
  }
})()

如果您可以将客户端代码包装在with()语句中,那么这显然只适用于此;尽管在许多情况下,这不应该很难。显然,同样的方法可以使用另一个带有所有属性的对象来屏蔽window,并使用getter代理的eval。

不支持SpiderMonkey的get语句的环境可能能够使用ES5的defineProperty。请自行了解。


勘误:在 proxied} 中缺少一个 } - johnjohn
2
在 Stack Overflow 上,你可以编辑其他人的答案以提出更改建议。这只是作为将来参考。但还是谢谢!问题已修复。 - ELLIOTTCABLE
很棒的想法。但似乎是一种可怕的做法。 - Oriol
@Oriol 天啊,我忘记我写过这个了。哇,那是一些高级邪恶。 - ELLIOTTCABLE

2

我尝试在FireFox 3.6.2中进行此操作,看起来可以正常工作。

我直接在FireBug命令行中输入了以下内容:

var proxied = eval;
eval = function() { alert("ha"); return proxied.apply(this, arguments);};
eval(7);

3
这种方法在不同的浏览器中无法可靠地运行,即使在Firefox中,使用代理版本也会缺少bobince所提到的特殊功能。 - T.J. Crowder
批处理未定义 [在此错误处中断] eval(7); FF 3.6.3pre - ko1ik
我知道它本来不应该工作。我只是说它似乎有效。 - Mark Lutton

0
也许我没有正确理解问题,但是我通过创建一个将原始的eval()函数放在内部并在myEval()中执行其他步骤的myEval()函数,来“覆盖”eval()
function myEval = function(value){
    //do your stuff here
    //for example
    try {
        value = eval(value)
    } catch (error) {
        console.log(error)
        value = NaN
    }
    return value
}

你应该为你发布的代码提供一些解释。 - Ricardo Pontual

-1

不仅你不应该这样做,而且我认为你可能做不到。首先,eval是一个全局函数,因此不是window的成员(就像你上面尝试的那样)。其次,作为全局函数,它很可能已经被硬编码到VM中,无法被覆盖。


2
实际上,无论是第三版规范(1999年)还是第五版规范(2010年),都将其称为全局对象的属性(在浏览器中为 window),并列在第15.1.2.1节中。但确实如此实现的可靠性不高。 - T.J. Crowder
TJ是正确的,所有全局变量都是window对象的属性。此外,硬连到VM?没有VM,JavaScript不会编译成某些VM字节码。最后,您可以覆盖大多数全局函数或属性,eval只是特殊的。 - Ruan Mendes
“没有虚拟机” - 这是非常不正确的。许多浏览器使用JS VM,例如TraceMonkey(火狐)或V8(谷歌),那些是什么?它们不是解释器,因为它们将JS代码编译成机器码。但从技术上讲,它们也不是编译器,因为它们不会暴露整个机器。它们向代码公开了一个子集(虚拟机)。如果这不是VM……那到底是什么?“Eval只是特殊的” - 啊,所以我是正确的。 - Timothy Baldridge

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