0

Very interesting. Why do you think?

var fn1 = function() {
    function fn2() {
        return "fn2 initialize...."
    }

    if (false) {
        function fn2() {
            return "fn2 if --> false"
        }
    }
    return fn2();
}

fn1();  // "fn2 if --> false"

or more interesting;

var fn1 = function() {
    function fn2() {
        return "fn2 initialize...."
    }

    if (false) {
        function fn2() {
            return "fn2 if --> false"
        }
    }
    return fn2();

    function fn2() {
        return "fn2 return after"
    }
}

fn1();  // "fn2 return after"
JJJ
  • 32,902
  • 20
  • 89
  • 102
yasaricli
  • 2,433
  • 21
  • 30
  • 2
    What is your question? What's happening here is that JavaScript *hoists* function definitions up to the top of the function scope. JavaScript has no block scope - so the `if` statements do not form their own scopes. The functions inside the `if` statements get hoisted up to the top of the closure function. The solution? Don't use function declarations (use function expressions instead), and don't define any `var`s inside a non-function block. Put all your `var` declarations for each function at the top of that function, so the hoisting is explicit. – sbking Dec 19 '13 at 08:15
  • 3
    @Cuberto: *"The functions inside the if statements get hoisted up to the top of the closure function."* Not necessarily. Some engines reject the whole thing as a syntax error (it is one, according to the current spec); others rewrite the declarations as expressions, not hoisting them. Firefox's (mumble)Monkey engine rejected these for a long time (now it rewrites them as expressions). – T.J. Crowder Dec 19 '13 at 08:22
  • 3
    *"Why do you think?"* Because I like it :) – Felix Kling Dec 19 '13 at 08:24
  • @T.J.Crowder Ah, well anyway, this can be avoided using the advice in the second part of my comment. – sbking Dec 19 '13 at 08:24
  • 1
    @Cuberto: Indeed. :-) – T.J. Crowder Dec 19 '13 at 08:25
  • Related question: [May function declarations appear inside statements in JavaScript?](http://stackoverflow.com/a/4071439/218196). You should definitely read the accepted answer, it's a interesting explanation around this issue. – Felix Kling Dec 19 '13 at 08:27
  • I'd gladly up-vote the question, but I think it should emphasize on the issue. Not everyone is aware how JS engines work and interpret the code examples, so it is not clear what is being asked. – Ivaylo Slavov Dec 19 '13 at 12:01

1 Answers1

10

There are two things happening in that code, one of which is specified behavior, and the other which is syntactically invalid (for now) and the results of which will vary by JavaScript engine.

The invalid bit is that you can't have a function declaration within a conditional block. E.g., this bit is invalid:

if (false) {
    function fn2() {
        return "fn2 if --> false"
    }
}

Some engines will treat it as a function declaration, meaning it's not subject to the flow of the step-by-step code (because function declarations aren't, they happen before the step-by-step flow).

Other engines will (in effect) rewrite that as a function expression for you, putting it in the step-by-step flow.

I believe ECMAScript6 will be addressing this.

The specified bit relates to just having two declarations within the same scope, e.g.:

var fn1 = function() {
    function fn2() {
        return "the first fn2"
    }

    return fn2();

    function fn2() {
        return "the second fn2"
    }
};
fn1();  // "the second fn2"

The specification clearly states that all function declarations within a scope are processed in order of the source code, and so the above (with the invalid bit removed) reliably uses the second fn2, not the first.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • You may wish to review the phrase "syntactically invalid" after reading (again?) [FunctionExpression's and memory consumptions](https://groups.google.com/group/comp.lang.javascript/msg/6eb53953ca5ce6eb?hl=en). Function statements are allowed in ECMAScript, unless you have a reference in ES5 that precludes them. – RobG Dec 19 '13 at 08:38
  • 1
    @RobG: Which part are you referring to exactly? From that I read, function declarations are not statements and hence are not valid inside blocks (http://stackoverflow.com/a/4071439/218196). ES5 spec confirms this: http://es5.github.io/#x14 (note how Statement and FunctionDeclaration are listed separately). – Felix Kling Dec 19 '13 at 08:51
  • I'm referring to the part of Richard's post where he says `In fact it is a function statement; a syntax extension that is capable of allowing the conditional creation of a function because being a Statement it can be [evaluated] inside a block.` ES5 also recognises Function Statements where it warns against them: [Several widely used implementations of ECMAScript are known to support the use of FunctionDeclaration as a Statement](http://ecma-international.org/ecma-262/5.1/#sec-12). So while they certainly to be avoid, they aren't aren't syntactically incorrect. – RobG Dec 19 '13 at 09:00
  • @RobG: Oh I see. However I'd argue that this section in the spec actually implies that implementations *incorrectly* treat declarations as statements and thus it is syntactically incorrect, at least according to the spec. – Felix Kling Dec 19 '13 at 09:03
  • 1
    @RobG: I think you're misinterpreting that part of §12. It's **not** "recognizing" them (in the sense of defining them as part of the spec). It's acknowledging that there are implementations that support that non-specified, invalid behavior and recommending against doing so until the spec addresses it (which it probably will in ES6). Indeed, §12 is quite clear that function declarations *are not* statements, as it gives a list of what a statement is right at the beginning of the section, and *FunctionDeclaration* is not on that list. – T.J. Crowder Dec 19 '13 at 09:04
  • @T.J.Crowder: If only I could express my thoughts as well as you do :) I always enjoy reading what you write. Happy Holidays! – Felix Kling Dec 19 '13 at 09:05
  • @RobG: FWIW, my guess is that *FunctionStatement* will show up in ES6 once they have time to get there, and that it'll do what Firefox's (mumble)Monkey currently does: Effectively treat them as named function expressions. (I'm sure there will be some bookkeeping that makes them not quite NFEs, but...) – T.J. Crowder Dec 19 '13 at 09:06
  • @FelixKling: LOL And you! – T.J. Crowder Dec 19 '13 at 09:07
  • The point of the quote is that browsers may treat `{ function foo(){} }` as a statement, not a *FunctionDeclaration*, and ES5 does not say they are illegal or syntactically incorrect. If it was, there would be no need to mention them as a confirming implementation would be expected to throw an error. – RobG Dec 19 '13 at 09:17
  • @RobG: Yes, the spec *does* say that doing that is invalid, by not listing *FunctionDeclaration* (or more likely a form of *FunctionStatement*) in the list of statements and defining the semantics of them. Treating a function declaration as a statement is just as out of spec as treating `glarb` as a synonym for `this`. Nowhere in the section on function declarations is there anything allowing them to happen as part of the step-by-step execution of the code. Doing that is invalid. It's probably useful, or it would be if engines agreed on what to do, but that doesn't make it in-spec. – T.J. Crowder Dec 19 '13 at 09:30
  • @T.J.Crowder—the spec is recognising their existence and could at that point have declared them illegal or invalid or whatever, but it doesn't. They are not allowed in strict mode. Note that `foo : function bar(){alert('bar')}` is another (less controversial) case of a function statement ([Brendan Eich](https://mail.mozilla.org/pipermail/es-discuss/2012-August/024750.html) calls it a "a labeled named function expression statement"). It is not allowed in strict mode either. – RobG Dec 19 '13 at 09:41
  • @T.J.Crowder—if they were not allowed at all then browsers would be expected to throw an error and there would be no need to mention them in ES5 at all. Function statements are expressly forbidden in strict mode. If they weren't allowed in non–strict mode there would be no need to do that. – RobG Dec 19 '13 at 09:45
  • @RobG: The absense of "hey, look at this. Don't do that, it's illegal" does not make something in-spec behavior. :-) And (mumble)Monkey did indeed used to throw a syntax error on these; now it's anticipating the next spec. I'm calling time on this. It's quite clear if you read the spec that there is no function statement, and there is nothing specifying using function declaration as a statement, and so doing so is completely out-of-spec. There probably will be in the next spec. Meanwhile, some engines do something that could be useful that isn't in-spec. Engines do that kind of thing a lot. – T.J. Crowder Dec 19 '13 at 09:46
  • @T.J.Crowder–I didn't say they were in the spec, but that they are allowed as a syntax extension. ES5 explicitly mentions them and sees fit to warn against them due to inconsistent support but does say "these are illegal" because they aren't. If they were not allowed at all, why are they expressly forbidden in strict mode (your "hey, look at this" moment)? ECMAScript allows function statements as a syntax extension. A language specification reference and quote from Brendan Eich should be sufficient evidence of that. – RobG Dec 19 '13 at 10:18