Understanding JavaScript Scope, Hoisting, and Execution Context
// Equivalent to:
function _demo() {
var val;
console.log(val); // undefined
val = 100;
}
```
The line `var val = 100;` performs two actions: declaring the variable `val` and assigning the value `100` to it. The first code example above behaves like the `_demo` function. Note that a declared but unassigned variable holds the value `undefined`, while accessing a completely undeclared variable throws a `ReferenceError`.
-
When a function is invoked, the JavaScript engine processes the function code in two phases. In the first phase, the engine scans the code and performs three tasks:
- Declares and initializes function parameters
- Declares local variables
- Declares and initializes inner functions
-
Execution context and execution context object The execution context represents everything that happens during the invocation of a function. The execution context object is an enternal object
{}that stores the variables and functions of that context. Each function call creates a new execution context object, which cannot be accesed directly.Below is an illustration of how the execution context object changes during the first phase when
execute(1)is called. Notice that inner functions are not executed and local variables are not yet assigned in the first phase.execute(1); // execution context object: {} function execute(param) { // execution context object: {param: 1} var innerVar = 'foo'; // execution context object: {param: 1, innerVar: undefined} function innerFunc() { // execution context object: {param: 1, innerVar: undefined, innerFunc: function(){...}} console.log('inner'); } innerFunc(); // first phase does NOT execute this line }When an inner function that is defined within the current execution context is called, a new execution context is created inside the current one. For example:
var globalVar = 1; function outerA() { var globalVar = 2; console.log(globalVar); // 2 outerB(); // 1 } function outerB() { console.log(globalVar); } outerA();Lexical scoping means that the scope is determined when the function is defined, not when it is called. Therefore, even though
outerBis invoked insideouterA, it cannot access the variables local toouterA. If we move the definition ofouterBinsideouterA, the behavior changes:var globalVar = 1; function outerA() { var globalVar = 2; console.log(globalVar); // 2 function outerB() { console.log(globalVar); } outerB(); // 2 } outerA(); -
Scope chain: When nested environments exist, variable scopes form a scope chain. To resolve a variable, the JavaScript engine first looks in the current execution context. If it is not found, the engine traverses up the scope chain until the variable is found or the global scope (
windoworglobal) is reached. Global variables are stored as properties of the global object; a global variableasatisfiesa === window.a.