Variable hoisting inside IIFE (lazy parsing)
Asked Answered
F

1

6

I am getting a very strange output on the below scenarios:

function test(){
   var test=123;
   console.log(test)
}
// this output : 123

(function test(){
   var test=123;
   console.log(test)
})()

// this output: 123 

But when using the below code

(function test(){
   test=123;
   console.log(test)
})()

//output:

function test(){
    test=123;
    console.log(test)
}

Can anybody please explain.

Fortunato answered 11/3, 2017 at 18:21 Comment(10)
It's called hoisting. Google it.Antipathetic
@Kinduser: No, that's not hoisting.Korman
I know hoisting, can you just please explain the behaviourFortunato
@T.J.Crowder Declaring variable inside function without var keyword makes it global, it is being hoisted up the script. Isn't it?Antipathetic
@Kinduser: Nope. :-) test within the IIFE refers to the function. (I think you meant "without var")Korman
@T.J.Crowder Yes, it does, because the test variable has been hoisted.Antipathetic
@T.J.Crowder: but he assigns to test. How come it doesn't have effect?Rummel
@Kinduser: No. Again, there's no hoisting there. The creation of the identifier is from the function expression. Hoisting only kicks in for var and function declarations. (And sort of half-way for let and const.)Korman
@AlonEitan: Definitely not a duplicate of that.Korman
@T.J.Crowder Thanks for letting me know. Retracted my voteEldenelder
K
9

What you're seeing isn't related to hoisting.

Your first example is quite straightforward:

(function test(){
   var test=123;
   console.log(test)
})()

You're creating a variable (via var) called test within the scope of the function, and assigning it a value, then outputting that value.

Your second example leaves out var:

(function test() {
    test = 123;
    console.log(test);
})();

...and so test has quite a different meaning: Within a function created by a named function expression, the function's name is an identifier resolving to the function. So test in that code is an identifier for the function.

That identifier is read-only when you've used a function expression, so your test = 123; line is ignored, and the console.log line outputs a representation of the function (as though test = 123; weren't there at all).

I'd forgotten that identifier is read-only (when created by an expression), but it is: From the specification:

FunctionExpression : functionBindingIdentifier ( FormalParameters ) {FunctionBody}

  1. If the function code for FunctionExpression is strict mode code, let strict be true. Otherwise let strict be false.
  2. Let scope be the running execution context's LexicalEnvironment.
  3. Let funcEnv be NewDeclarativeEnvironment(scope).
  4. Let envRec be funcEnv's EnvironmentRecord.
  5. Let name be StringValue of BindingIdentifier.
  6. Perform envRec.CreateImmutableBinding(name, false).
  7. ...

Note Step 6: The binding creating the identifier is immutable (can't be changed).

Note that this isn't true for the identifier (binding) created by a function declaration, which is mutable; but function expressions and function declarations treat the identifiers created by the function name completely differently. (For instance: A function declaration puts the name in scope where the declaration is, but a function expression doesn't.)

Korman answered 11/3, 2017 at 18:27 Comment(4)
Do you know why it is read-only, however without IIFE its overriding the value to 123Fortunato
@ankurkushwaha: in the snippet without IIFE you also use varRummel
@ankurkushwaha: in fact, IIFE is irrelevant here. It's the absence or presence of var that makes the difference.Rummel
I agree with @ankurkushwaha, why does it work properly when theres no iife? And even with iife, it works when theres var keyword. Why does it happen?Antipathetic

© 2022 - 2024 — McMap. All rights reserved.