To understand how functions work in JS, lets see how JS works and how code is executed behind the scenes.
Execution context:
JS is a synchronous single threaded language, means that it executes the code one line at a time and only in specific order, meaning if current line of code is executed then only it moves to next line and executes the next line of code.
The whole JavaScript code is executed in "Execution Context". It is the environment or container where the JS code is executed. Execution context has 2 components, namely "memory component "also known as "variable environment "and "code component" also known as "Thread of execution". In memory component , all the variables and functions are stored in it as key value pairs. In code component the execution is happened one line at a time.
When JS code is executed, there are two phases - MEMORY CREATION PHASE and CODE EXECUTION PHASE. These phases happen inside execution context. In memory creation phase , the JS skims through whole program and allocates memory to all the variables and functions in form of key value pairs. Initially JS gives a spatial value called "UNDEFINED" to the variables and in functions., the whole code of functions is stored.
In code execution phase , the JS goes line by line and executes the code. In this phase the actual value of variables are stored. When there is a function invocation or when we run a function, a new execution context is created and there will be again memory allocation and code execution phase inside it.
JS manages a stack to manage all these execution contexts which is known as call stack. Whenever a program is run , initially a stack is generated with global execution context and the execution contexts are pushed inside the stack , once the execution context is completed it pops out of the stack and the control goes back to global execution context and when the whole execution is completed , the GEC also pops out of the stack and call stack gets emptied.
"CALL STACK MAINTAINS THE ORDER OF EXECUTION CONTEXTS"
Hoisting:
Variables and function declarations are moved to the top of their scope before code execution.
Even before code starts executing, the memory is being allocated to the variables and functions(EXECUTION CONTEXT) , so if we try to access a variable after initialization it gives you undefined, if we try to access the variable even before initialization it throws reference error, saying that variable is not defined.
So this memory allocation even before declaring variables is known as hoisting in JavaScript.
Only variables declared with "var" are hoisted
Arrow functions behave like variables and if we try to access it, gives undefined value. Only in normal functions it will copy the code and store it.
How functions work?
A global execution context is created when function is called and hoisting comes into picture by skimming the code and allocating memory first and then execute the code line by line.
Each and every function is pushed inside the call stack, it gets executed and pops out of call stack.
After the whole code gets executed then the global execution context is also popped out of call stack.