无法评估函数定义(...在 JavaScript 中)

Trouble Eval'ing Function Definitions (...in JavaScript)

我正在尝试通过评估用户提供的字符串来构建一个简单的 REPL。它似乎在大多数情况下都有效,除了像 "function f() {...}" 这样的输入,它对未来评估中哪些函数可见没有影响。玩了一会儿之后,我只能得出结论,我根本不了解 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 等工具中执行时,最后一次调用才会失败,但嵌入脚本标记时不会。对已接受答案的评论包含对此的解释。

我会尽力解释:

在全球范围内评估良好:

good();         // Okay.

Alsogood 没有 var 定义,因此是在全局范围内定义的:

alsoGood();     // Okay.

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

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

Hope 是在全局范围内通过 eval 的闭包引用求值的,因此它是在全局范围内求值的:

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

CrushedHope和hope一样,但是这里应该定义silly:

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

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

这是 ECMAScript5 的一个特性。至 quote MDN:

If you use the eval function indirectly, by invoking it via a reference other than eval, as of ECMAScript 5 it works at global scope rather than local scope; this means, for instance, that function declarations create global functions, and that the code being evaluated doesn't have access to local variables within the scope where it's being called.

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

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