JavaScript programming can’t get around declaring variables and functions, but how and where does the interpreter find them? Next, we’ll continue our JavaScript series on Execution Contexts and Stacks to take a closer look at execution contexts through the introduction of Variable objects.
As mentioned in the previous article, the life cycle of an execution context can be divided into three phases:
Detailed understanding of execution context is very important for beginners, because it involves variable objects, scope chain, this and many other JavaScript beginners do not fully understand, and very important concepts, it is related to whether we can truly understand JavaScript, truly understand can also be more easily competent for the follow-up work, We’ll cover them in more detail in a later article, but let’s focus on variable objects.
The variable object
A Variable Object is a data scope that is specific to the execution context. It stores Variable and function declarations defined in the context.
function foo(){
var a = 10;
function b(){},function c(){});
console.log(a); / / 10
console.log(b); // function b(){}
console.log(c); // Uncaught ReferenceError: c is not defined
}
foo();
Copy the code
In the above example, the variable object of function foo () contains declarations of variable A and function b (). One thing to note here is that function expressions are not contained in variable objects as function declarations are, and accessing c () functions results in reference errors, as seen in the example. Because the variable object is abstract and special, it cannot be accessed in code, but is handled by the JavaScript engine.
The above uses variable objects in the function context to show what they store, but variable objects also exist in the global context. Let’s talk about variable objects in the global context and function context respectively.
Global context
For example, in the browser, the global object is Window. A special feature of the global context is that its variable object is the Window global object. And this is also true for this, which also refers to the window.
For example, in a browser, the global object is Window
// Global context creation phase
// VO is short for Variable Object
windowEC = {
VO: Window,
scopeChain: {},
this: Window
}
Copy the code
In addition, the lifetime of the global context is the same as the lifetime of the program, as long as the program does not end, such as closing the browser window. All other contexts have direct access to the properties of the global context.
Function context
As mentioned above, variable objects store variable and function declarations in the execution context, but in the function context, arguments also add a pseudo-array object.
The creation phase of the variable object will include:
- Create arguments objects. Examine the parameters in the current context and establish the properties and property values under the object.
- Check for function declarations in the current context, that is, functions declared using the function keyword. Creates an attribute with the name of the function in the variable object and the value of the attribute is a reference to the memory address of the function. If the variable object already has an attribute of the same name, replace the attribute completely.
- Check variable declarations in the current context(
var
Declared variable), the default isundefined
; If the variable name is the same as an already declared formal parameter or function, to prevent the function with the same name from being modified toundefined
, the variable declaration will be skipped and the original attribute value will not be modified.
You may have a doubt about the word “skip” in point 3, right? In the following example, foo declared as a variable will skip foo declared as a function. Why is foo’s output still overwritten?
function foo() { console.log('I am function foo')}var foo = 10;
console.log(foo); / / 10
Copy the code
The reason is simple, because the above three rules only apply to the creation of variable objects, that is, the creation of execution contexts. Foo = 10 is run during execution in the execution context, and the output is naturally 10. Consider the following example:
console.log(foo); ƒ foo() {console.log('I am function foo')}
function foo() { console.log('I am function foo')}var foo = 10;
console.log(foo); / / 10
Copy the code
Why is it different? In fact, it is executed in this order:
// First put all function declarations into the variable object, function declarations are promoted
function foo() { console.log('I am function foo')}// Next, all variable declarations are put into variable objects, but since foo already has a function of the same name, assignment of undefined by default is skipped
// var foo = undefined;
// Then start executing the code in the execution phase
console.log(foo); ƒ foo() {console.log('I am function foo')}
// Run during execution of the execution context
foo = 10;
console.log(foo); / / 10
Copy the code
Following the above rules, it is easy to understand how variable promotion works, and we can see that function declarations take precedence over var declarations. In order to help you better understand the variable object, let’s combine a simple example to discuss.
function test() {
console.log(a);
console.log(foo());
var a = 1;
function foo() {
return 2;
}
}
test();
/* Undefined 2 */
Copy the code
According to the above rules, after understanding the variable promotion, the execution order can be understood as:
function test() {
function foo() {
return 2;
}
var a;
console.log(a);
console.log(foo());
a = 1;
}
test();
Copy the code
Is that obvious?
It is also important to note that no properties in the variable object can be accessed until the function is executed! After the execution phase, however, the variable object (VO) is transformed into an active object (AO), and then the execution phase operations begin.
Execution phase
The variable object (VO) is activated into the active object (AO), and the properties in the object can be accessed. The function executes the code sequentially and changes the property value of the variable object. The execution context code in this stage is divided into two stages:
- Enter execution context
- Execute the code
Enter execution context
When the execution context is entered, the code has not yet been executed. Let’s look at an example:
function foo(a, b) {
var c = 10;
function d() {}
var e = function _e() {};
(function x() {});
}
foo(10);
Copy the code
When entering the context of foo with argument 10, the AO looks like this:
AO = {
arguments: {
0: 10.1: undefined.length: 1
}
a: 10.b: undefined.c: undefined.d: <function reference to d>,
e: undefined,
}
Copy the code
X is a function expression, so it’s not in a variable object, and the value referenced by e is also a function expression, so e itself is a declaration, so it’s in a variable object.
Execute the code
This phase executes the code sequentially, modifying the attribute values of the variable object, following the example above, with the following AO:
AO = {
arguments: {
0: 10.1: undefined.length: 1
}
a: 10.b: undefined.c: 10.d: <reference to function declaration d>,
e: <reference to Function expression to _e>,
}
Copy the code
This is the end of the variable object creation process, let’s briefly summarize:
- The variable object initialization of the global context is the global object
- Function context variable object initializations include only
Arguments
object - Initial attribute values such as parameters, function declarations, and variable declarations are added to variable objects in the execution context
- Properties in the variable object cannot be accessed until the function is executed
- During the code execution phase, the attribute values of the variable object are modified again and assigned to the existing attribute values
If you find this article helpful, you are welcomeMy GitHub blogLike and follow, thank you!