为什么即使我之前返回,第二个函数声明也会获胜?

And*_*eas 7 javascript

我有以下JavaScript代码:

(function() { 
    function f(){ alert(1); }
    return f();
    function f(){ alert(2); }
})();
Run Code Online (Sandbox Code Playgroud)

你能解释为什么警报弹出2而不是1吗?

谢谢,

T.J*_*der 9

这将进入执行进入函数时发生的事情:抛出大量细节,处理所有函数声明(您使用过的样式),并且只有之后才会执行逐步执行代码.所以你的return陈述对选择哪个函数声明没有影响.并且所选择的声明始终是源代码顺序中的最后一个(这包括 - 在规范的第10.5节中非常粗糙的散文中).

如果使用函数表达式,结果将根本不同,函数表达式作为逐步代码的一部分进行评估:

(function() { 
    var f;
    f = function(){ alert(1); };
    return f();
    f = function(){ alert(2); };
})();
Run Code Online (Sandbox Code Playgroud)

这段代码在技术上是不正确的(因为你return总是会遵循一个跟随的代码),但它说明了差异:你看到的是alert(1)发生而不是alert(2),因为这些表达式在达到之前不会被评估.

您可以在规范的10.4.310.5节中详细了解执行进入函数时所发生的事情(声明不是在第一步循序代码之前完成的唯一事情).


随着你的新知识,一个小测验:这里发生了什么?(注意:永远不要这样做.)

function foo() {

    if (true) {
        function bar() {
            alert(1);
        }
    }
    else {
        function bar() {
            alert(2);
        }
    }

    bar();
}

foo();
Run Code Online (Sandbox Code Playgroud)

答案是:它有所不同,不要这样做.一些引擎将使用第一个bar,其他引擎将使用第二个,而其他引擎将其称为语法错误.这是因为这实际上一个错误,因此引擎可以自由地做他们认为最好的事情.如果仔细查看语言语法,您会发现将函数声明放在其包含的直接范围内的分支中是无效的.它们必须处于该范围的顶层.通过对声明的新理解,原因应该是显而易见的:它们与范围内的执行流程无关,因此您无法根据执行流程选择它们.

那么现实世界会发生什么?可悲的是,如果你处于"松散"模式(不严格),通常不会出错.一些引擎(例如Chrome的V8,在撰写本文时)将忽略流控制语句,只选择最后声明的函数(因此你得到第二个 bar函数使用的反直觉结果),其他引擎(Firefox的SpiderMonkey, IE11的JScript)有效地重写您的代码,将这些声明转换为表达式,因此您获得第一个bar.例如,它会因引擎而异.好消息是,如果你在严格模式下尝试这个,那么所有这三个(V8,SpiderMonkey和IE11的JScript)都会失败而不是选择一个(V8和SpiderMonkey在控制台中有明确的错误消息; JScript只是令人惊讶的" bar未定义",但......).

如果你想要做的事就像上面,但有效和整个发动机保持一致,使用表达式:

function foo() {
    var bar;

    if (true) {
        bar = function() {
            alert(1);
        };
    }
    else {
        bar = function() {
            alert(2);
        };
    }

    bar();
}

foo();
Run Code Online (Sandbox Code Playgroud)

kangax的Named Function Expressions Demystified页面上有一个有趣的探索.