0

Hello everyone so i ran into this problem

function test() {
  test2();
  if (true) {
    function test2() {
      console.log(a);
    }
    let a = 4;
  }
}

I checked on the debugger to find out why it behaves the way it does and I have seen that test2 function got hoisted but it was set to undefined

My question is why? when a function isn't inside a block and I call it before the declaration it will work because of the creation phase so why in this case it doesn't?

Manas Khandelwal
  • 3,790
  • 2
  • 11
  • 24
  • [Function declarations inside if/else statements?](https://stackoverflow.com/questions/10069204/function-declarations-inside-if-else-statements) – Andreas Mar 27 '21 at 09:44
  • Because you can't use function if you declarated it in another visibility area – Petrashka Siarhei Mar 27 '21 at 09:44
  • 1
    @СергейПетрашко - It's more complicated than that. :-) In the OP's code, they could use `test2` *after* the `if` block, assuming they're in loose mode (there's no `"use strict"` above, but for all I know it's module code and already strict). – T.J. Crowder Mar 27 '21 at 09:53
  • 1
    @T.J.Crowder , I agree with you!. I was writing that for give direction) – Petrashka Siarhei Mar 27 '21 at 09:57
  • You are calling a function that does not exist yet. This is how works js. It's all about context and scope, line by line executed and the scope is populated. – NVRM Mar 27 '21 at 10:01

1 Answers1

2

Function declarations inside blocks are different from function declarations at the top level of a scope, because...they're in a block. :-) They were only recently (ES2015) and partially standardized; prior to that, supporting them was an allowed extension to the specification, but unfortunately different JavaScript engines supported them slightly differently (and sometimes their support varied over time), so what got standardized had to be the intersection of things that behaved basically the same way across the various implementations.

Your example is one of those that behaved the same way, though, so we can say what the spec says about it. The function isn't assigned to the test2 identifier until code execution enters the if block. Here's your code:

function test() {
    test2();
    if (true) {
        function test2() {
          console.log(a);
        }
        let a = 4;
    }
}

test();

...and here's roughly how it's handled in loose mode (there's a very minor difference in strict mode):

function test() {
    var test2; // Identifier declared and initialized with `undefined` here
    test2();
    if (true) {
        test2 = function test2() { // Function assigned here, at the top of the block
          console.log(a);
        };
        let a = 4;
    }
}

test();

As you can see, since the function hasn't been assigned to the test2 var yet, you can't call it where you have the call.

It's not clear from your code, but the assignment to the test2 identifier is hoisted to the top of the block where the function declaration appears. We can see that if we don't try to call it too early and log the value in test2 prior to the block (where it's undefined) and inside the block above the declaration (where it's the function):

function test() {
    console.log(test2);         // undefined
    // test2();
    if (true) {
        console.log(test2);     // the function - assignment was hoisted
        function test2() {
          console.log(a);
        }
        let a = 4;
    }
    console.log(test2);         // still available here!
}

test();

The slight difference in strict mode is primarily that the identifier has let semantics instead of var semantics and is only scoped to the block:

"use strict";
function test() {
    // Can't use `test2` here at all
    if (true) {
        console.log(test2);     // Now it exists and has been initialized with the function
        function test2() {
          console.log(a);
        }
        let a = 4;
    }
    // Can't use `test2` here at all
}

test();

I always recommend using strict mode, and this is one of the reasons. To me, it just makes sense that a function declared within a block is only accessible within that block.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • (FWIW, I go into the new standardized function declarations in blocks in detail in Chapter 3 of my recent book *JavaScript: The New Toys*. Links in my profile if you're interested.) – T.J. Crowder Mar 27 '21 at 09:42