简体   繁体   中英

Why does second function declaration win even though I return before it?

I have the following JavaScript code:

(function() { 
    function f(){ alert(1); }
    return f();
    function f(){ alert(2); }
})();

Can you explain why the alert pops up with 2 and not 1?

Thanks,

This gets into what happens when execution enters a function: Leaving out a lot of detail, all function declarations (the style you've used) are processed, and only after that does step-by-step code execution occur. So your return statement has no impact on which function declaration is chosen. And the declaration chosen is always the last one in source code order (this is covered — in wonderfully turgid prose — in Section 10.5 of the specification).

The result would be fundamentally different if you used function expressions , which are evaluated as part of step-by-step code:

(function() { 
    var f;
    f = function(){ alert(1); };
    return f();
    f = function(){ alert(2); };
})();

This code is technically incorrect (because you have code following a return that will always be followed), but it illustrates the difference: You see the alert(1) happen rather than the alert(2) , because those expressions are not evaluated until they're reached.

You can read more about what happens when execution enters a function (declarations aren't the only thing that are done prior to the first step-by-step code) in Sections 10.4.3 and 10.5 of the specification.


And with your new knowledge, a quiz: What happens here? (Note: Never do this.)

function foo() {

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

    bar();
}

foo();

The answer is: It varies, don't do that. Some engines will use the first bar , other engines will use the second, and others will call it a syntax error. This is because this actually is an error, and so engines are free to do what they think best. If you look closely at the language grammar, you'll see that it's invalid to put function declarations inside branches within their immediately-containing scope. They must be at the top level of that scope. With your new understanding of declarations, the reason should be obvious: They're not related to the flow of execution within the scope, and so naturally you can't choose them based on that flow of execution.

So what happens in the real world? Sadly, usually not an error if you're in "loose" mode (not strict). Some engines (Chrome's V8 for example, as of this writing) will ignore the flow control statements and just pick the last function declared (and so you get the counter-intuitive result that the second bar function is used), other engines (Firefox's SpiderMonkey, IE11's JScript) effectively rewrite your code on the fly turning those declarations into expressions instead, and so you get the first bar . Eg, it will vary by engine. The good news is that if you try this in strict mode, all three of those (V8, SpiderMonkey, and IE11's JScript) will fail rather than picking one (V8 and SpiderMonkey with nice clear error messages in the console; JScript with just the surprising " bar is undefined", but...).

If you want to do something like the above, but valid and consistent across engines, use expressions:

function foo() {
    var bar;

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

    bar();
}

foo();

There's a fun exploration of this on kangax's Named Function Expressions Demystified page .

There is something that is called "hoisting" - it's a corollary of scope/function activation resolution process (without going into too much details).

What that means is that at entering the scope of the function of variable and function declarations are processed and stored as if they were made at the beginning of the scope. The assignment parts are left where they are.

So the following code:

function()
{
    console.log(a)
    var a = 5;
}

will print "undefined", and that is because, simply put, it is equivalent to:

function()
{
    var a;
    console.log(a)
    a = 5;
}

In you example - there is an important thing to remember - these are function declarations - so they are processed as any variable would be, but since they "contain" the function body, then the whole declaration (including equivalent of a = 5 ) is "hoisted".

During that variables processing stage - if there already has been processed a variable of the same name - it's value and attributes are replaced.

So you code is equivalent to:

(function() { 
    function f(){ alert(2); }
    return f();
})();

Because the body of the anonymous function isn't procedural as you might expect.

Rather it's an object with properties. And you're allowed forward references - that is, code in one function can refer to properties that are defined later on in the object.

Since you have duplicate definitions of the same property, the latter one takes precedence, effectively overwriting the first. So your snippet is equivalent to (removing the first, overridden definition):

(function() { 
    return f();
    function f(){ alert(2); }
})();

and at this point it should come as no surprise to you that the alert contains 2 .

因为代码首先被解析,所以f()函数被承保,而不是执行,并调用最后一个f()函数。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM