麻烦评估功能定义(...在JavaScript中)

时间:2016-01-13 14:55:41

标签: javascript function eval

我试图通过评估用户提供的字符串来构建一个简单的REPL。它似乎在很大程度上起作用,除了像"函数f(){...}"这样的输入,它们对未来的evals中可见的函数没有影响。在玩了一下之后,我只能得出结论,我根本不理解eval。这是一个简短的片段,展示了一些神秘的行为:

var xeval = eval;

function silly() {}

eval("function good() {}");

function baffleMe() {
    eval("alsoGood = function() {}");
    eval("function notSoGood() {}");
    xeval("function hope() {}");
    xeval("function crushedHope() { silly(); }");
}

baffleMe();

good();         // Okay.
alsoGood();     // Okay.
notSoGood();    // ReferenceError: notSoGood is not defined
hope();         // Why does this even work?
crushedHope();  // ReferenceError: silly is not defined

有人能解释这些结果吗? (可在最新的Chrome和Firefox中重现)

[编辑]

为了澄清,最后一次调用仅在代码在Javascript控制台或JSFiddle等工具中执行时才会失败,但在嵌入脚本标记时则不会。对已接受答案的评论包含对此的解释。

2 个答案:

答案 0 :(得分:1)

我会尝试解释:

在全球范围内评估好:

good();         // Okay.

Alsogood没有var定义,因此正在全球范围内定义:

alsoGood();     // Okay.

NotSoGood在函数范围内定义,因此在全局范围内不存在:

notSoGood();    // ReferenceError: notSoGood is not defined

希望通过eval对全局范围的闭包引用进行评估,因此它在全局范围内进行了评估:

hope();         // Why does this even work?

CrushedHope与希望相同,但在这种情况下应该定义愚蠢:

crushedHope();  // ReferenceError: silly is not defined

正如下面的评论中所提到的,在使用JSFiddle和代码用window.onload包装时,问题中未定义愚蠢。

答案 1 :(得分:1)

这是ECMAScript5的一项功能。致quote MDN

  

如果您使用eval函数间接,则通过非eval之类的引用调用它,从ECMAScript 5开始,它在全局范围而不是本地范围内工作;这意味着,例如,函数声明创建全局函数,并且被评估的代码无法访问被调用的范围内的局部变量。

因此直接调用eval()会在本地范围内创建函数。 (使用a=something方法会自动创建一个全局。)但是当您通过xeval()间接调用它时,它会被全局评估。

silly未定义的最终结果取决于您正在评估的范围。如果您在控制台中,它似乎有不同的范围。如果您创建测试HTML页面,它可以正常工作。