Execution Context and Scope Chain in JavaScript
Description
Execution Context is the environment in which JavaScript code executes, containing key information such as variables, functions, and scope chain. Scope Chain is a mechanism used to resolve variable access permissions, consisting of the variable object of the current execution context and all outer contexts' variable objects. Understanding these two concepts helps in mastering core concepts like variable lookup and closures.
Step 1: Types of Execution Contexts
There are three types of execution contexts in JavaScript:
- Global Execution Context: Created when the code runs for the first time, globally unique, with a lifecycle consistent with the program.
- Function Execution Context: Created each time a function is called and destroyed after the function finishes execution.
- Eval Execution Context (rarely used): Created when code inside the
evalfunction executes.
Each execution context contains three core components:
- Variable Object (VO): Stores variables, function declarations, and parameters.
- Scope Chain: Consists of the current variable object and the parent scope chain.
thisvalue: Points to the object associated with the current execution context.
Step 2: Creation Process of Execution Context
Taking a function execution context as an example, creation is divided into two phases:
-
Creation Phase:
- Create the variable object:
- Scan function parameters, adding them as properties (values initialized to passed values or
undefined). - Scan function declarations, adding them as properties (values are function references, overriding parameters with the same name).
- Scan variable declarations, adding them as properties (values initialized to
undefined, without overriding functions or parameters with the same name).
- Scan function parameters, adding them as properties (values initialized to passed values or
- Build the scope chain: Copy the function's
[[Scope]]property (determined at definition time) and add the current variable object to the beginning of the chain. - Determine
thisbinding (determined by the calling method).
- Create the variable object:
-
Execution Phase:
- Execute code line by line, modifying values in the variable object.
- When encountering variables, look them up along the scope chain.
Example Analysis
var a = 1;
function foo(b) {
var c = 2;
console.log(a + b + c);
}
foo(3); // Outputs 6
-
Global Context Creation Phase:
- Variable object:
a: undefined,foo: <function> - Scope chain:
[global variable object] thispoints to the global object (e.g.,window).
- Variable object:
-
Global Context Execution Phase:
- Execute
a = 1, modifying the variable object toa: 1. - Execute
foo(3), creating the function context.
- Execute
-
fooFunction Context Creation Phase:- Variable object:
b: 3,c: undefined(parameterbtakes priority, variablecis initialized). - Scope chain:
[foo variable object, global variable object](copyfoo.[[Scope]]and add the current variable object). thisis determined by the call (here, default binding points to the global object).
- Variable object:
-
fooFunction Context Execution Phase:- Execute
c = 2, modifying the variable object toc: 2. - Execute
console.log(a + b + c):- Look up
a: Not found in the current variable object → Search along the scope chain to the global variable object, findinga=1. bandcare found directly in the current variable object.
- Look up
- Execute
Step 3: Principle of Scope Chain Formation
- The
[[Scope]]property of a function is determined at definition time, containing the variable objects of its outer scopes. - For example, the scope chain of a nested function includes the variable object of the outer function upon creation:
function outer() {
var x = 10;
function inner() {
console.log(x); // Accesses outer's x via the scope chain
}
return inner;
}
var innerFunc = outer(); // After outer executes, inner can still access x via the scope chain (closure)
Key Takeaways
- Execution contexts are created dynamically, while the scope chain is statically determined at function definition.
- Variable lookup proceeds from inner to outer along the scope chain; if not found, a
ReferenceErroris thrown. - The essence of closures is that a function retains a reference to the scope chain at the time of its definition, even after the outer function has finished execution.