0

Please note: ^ This question is not a duplicate to a let/const question as that is unrelated. The same question remains regardless if it was var, let, or const since that is unrelated to the intent of the question.


I would really appreciate some clarification on function hoisting within another function. This is what I understand clearly so far:

This does not work because anotherFunc is being called in the file before it's created and const does not hoist before it's created.

const someFunc = () => {
 console.log('someFunc called');
}
   
anotherFunc(); // anotherFunc is not defined error
   
const anotherFunc = () => {
 console.log('anotherFunc called');
}

This works because anotherFunc is a function declaration and they are hoisted to the top as per JS rules:

anotherFunc(); // anotherFunc called

function anotherFunc() {
    console.log('anotherFunc called');
}

Doesn’t work because it’s a function expression and since they are assigned to a var variable, the variable will be hoisted to the top and undefined initially:

anotherFunc(); // anotherFunc is not a function at <anonymous>

var anotherFunc = function () {
    console.log('anotherFunc called');
}

So can someone explain to me why this works(see below)? This is a step deeper from everything explained online.

If a function is declared later but is called inside another function before it, why does it work? This has something to do with JS execution order?

const someFunc = () => {
    console.log('someFunc called');
    anotherFunc();
}
   
const anotherFunc = () => {
 console.log('anotherFunc called');
}

someFunc(); // 'someFunc called' 'anotherFunc called'

Is it because someFunc and anotherFunc are first assigned by JS in memory so then calling them later doesn't matter what order they are in?

Lastly, does this differ for const/let/var functions in the same scenario(the last one)?

Thank you so much for the clarification!

Mar
  • 115
  • 1
  • 3
  • 12
  • All that matters is that you call the function AFTER it has been defined. The ordering in the source code doesn't matter, just the timing. – Barmar Dec 24 '21 at 03:34
  • 2
    Since you call `someFunc` after both functions have been defined, `someFunc` can call `anotherFunc`. – Barmar Dec 24 '21 at 03:35
  • Thanks Barmar! But I'm wondering a bit deeper than that. Like what exactly happens in JavaScript? Why can `someFunc` call `anotherFunc` after it has been defined? Does JS store them first in memory and then when `anotherFunc` is called it's simply getting a function already available in that memory? Or what? What is the order of events, this is what I'm really curious about. – Mar Dec 24 '21 at 03:38
  • They're just variables. After a variable has been given a value, you can refer to it. – Barmar Dec 24 '21 at 03:39
  • When you execute `anotherFunc()`, it just looks up the name `anotherFunc`, gets its value, and calls it. – Barmar Dec 24 '21 at 03:40
  • It's no different from if you wrote `console.log(someVariable);` and the variable was assigned after the function. – Barmar Dec 24 '21 at 03:41
  • Well there is a difference in that case because if it's a `var` it would undefined, otherwise you would get errors for `const` and `let`. – Mar Dec 24 '21 at 03:42
  • No it wouldn't. Define the function, then define the variable, then call the function, and it works. – Barmar Dec 24 '21 at 03:43
  • 1
    `function foo() { console.log(variable); } let variable = "bar"; foo();` – Barmar Dec 24 '21 at 03:43
  • Ah that's what you mean. Yeah I see that works. But still I'm curious what happens under the hood. – Mar Dec 24 '21 at 03:46
  • Nothing special happens. When you do `const functionName = something` it evaluates the `something` expression and puts it into the variable `functionName`. You can then call the function after that's done. – Barmar Dec 24 '21 at 03:48
  • 1
    The important point is that JavaScript doesn't require names to be declared before you refer to them in function definitions. Only before the code that refers to them is executed. – Barmar Dec 24 '21 at 03:49
  • Function hoisting is a special case that allows you to refer to functions in the same scope before they're defined. – Barmar Dec 24 '21 at 03:50

1 Answers1

0

JavaScript's Execution Context has two components:

  • Memory (Variable Environment)
  • Code (Thread of Execution) - Sync & Single-Threaded meaning JS can only do things one by one and in a certain orders(one after the other)

So the reason this works:

const someFunc = () => {
    console.log('someFunc called');
    anotherFunc();
}
   
const anotherFunc = () => {
 console.log('anotherFunc called');
}

someFunc(); // 'someFunc called' 'anotherFunc called'

...is because JS first allocates someFunc and anotherFunc to undefined within memory(as I had suspected in my question). Function declarations are also saved here as the entire function.

This is the memory allocation phase. Then it runs the code execution phase.

JS goes lines by line and adds the values to the variables.

If the value for the key saved in memory is a function it creates another inner Execution Context and once again runs a Memory Allocation and Code Execution Phase for that particular function.

So in my case:

  1. Execution Context is created.
  2. someFunc and anotherFunc are allocated to memory as undefined.
  3. someFunc gets called and a new inner Execution Context is created.
  4. console.log runs.
  5. anotherFunc gets saved to undefined in memory within someFunc execution context. Code execution runs and anotherFunc gets called and another Execution Context gets created for that function. Since it has no variable etc. it just runs the console.log.
  6. anotherFunc is complete and its Execution Context gets removed. It gives its' power back to someFunc(this also happens when a function returns).
  7. someFunc is complete and it's Execution Context gets removed. It gives its` power back to the main JS Execution Context.(this also happens when a function returns).

This guy explains it all very clearly: https://youtu.be/iLWTnMzWtj4

Mar
  • 115
  • 1
  • 3
  • 12
  • `const`, `let` and `class` are not initialised with `undefined` when they are created, they are left uninitialised. – Bergi Dec 27 '21 at 20:26
  • Yes you are right. `const` and `let` are still hoisted though and created in memory but they are not initialized until the code is run. This called the "temporal dead zone". Which is why you would get a Reference error if you called them before they are declared. – Mar Dec 27 '21 at 21:45
  • Although I didn't know about `class`. Really? JS doesn't natively have classes, they are just functions underneath so I wonder why they don't get hoisted up, at least similarly to function expressions ie automatically set as `undefined`. – Mar Dec 27 '21 at 21:46
  • 1
    See [Why are ES6 classes not hoisted?](https://stackoverflow.com/a/35537963/1048572) :-) – Bergi Dec 27 '21 at 22:39
  • Ah ok, thanks! :) – Mar Dec 27 '21 at 23:42