It is important to understand the js execution process, such as scope, scope chain, variable promotion, and closure. To understand this, you need to understand what happens when functions are executed!

The Execution Context is also called the Execution Context

An execution environment is created when code is executed. There are three types of execution environments in JavaScript.

  1. Global environment: In browsers, the global environment is considered the Window object, so all global variables and functions are created as properties and methods of the Window object.
  2. Function environment: When a function executes, an execution environment for that function is created in which the code is executed.
  3. Eval (not recommended, can be ignored)

Function variables that are not declared using var are properties of window in non-strict mode, i.e. global variables.

Call Stack

Js determines the order of execution based on the invocation (execution) of functions. Every time a function is called, JS creates an execution environment for it, and the JS engine puts this execution environment on a stack to process.

This stack is called the Call stack. The bottom of the stack is always the global environment, and the top of the stack is the environment where the function is currently executing. When the execution environment at the top of the stack is finished, it exits the stack and passes execution to the previous execution environment.

See what the chestnut says:

function A(){
   console.log("this is A");
   function B(){
       console.log("this is B");
   }
   B();
}

A();
Copy the code

So this is what happens when this code executes.

  1. First of all A (); The A function executes, and the A execution environment is pushed.
  2. When A is executed, B() is encountered, B is executed again, and B is pushed.
  3. There are no more functions in B that can be executed. B runs out of stack.
  4. Continue to execute A, there are no more functions in A that can be executed, and A is done exiting the stack.

Here’s another unconventional one:

function A() {function B(){
        console.log(666);
    }
 
    returnB; } var C = A(); C(); / / 666Copy the code
  1. First of all A (); The A function executes, and the A execution environment is pushed.
  2. Continue to execute A, there are no more functions in A that can be executed, and A is done exiting the stack.
  3. Then C(), in which case C is B, executes A, assigns the return value of B to C, and the execution environment of B is pushed onto the stack.
  4. There are no more functions in B that can be executed. B runs out of stack.

For those of you who are sharp-eyed, you can see, is there a closure here? In fact, if you change it a little bit, it will form a closure.

function A(){
    
    var say = 666
    
    function B(){
        console.log(say);
    }
 
    returnB; } var C = A(); C(); / / 666Copy the code

So, that will form a closure, but we’re not going to talk about closures this time, just so you know how it’s implemented.

Three, the implementation process

We now know that every time a function is executed, a new execution environment is created. In fact, inside the JS engine, the creation process of this environment can be divided into two stages:

A. Setup phase (occurs when A function is called (executed), but before the specific code inside the function is executed)

  • 1. Establish activity objects;
  • 2. Build scope chain;
  • 3. Determine the value of this.

B. Code execution stage (executing specific code inside a function)

  • 1. Variable assignment;
  • 2. Execute other code.

Note that the Scope chain is created when the function is created. At this point, the chain has only the global variable object, which is stored in the [[Scope]] property of the function. Then, when the function is executed, the Scope chain is constructed by copying the object in the property. This is explained later in this article.

See picture more clearly!

If you think of the function execution environment as an object:

ExecutionContextObj = {// execute context object scopeChain: [AtiveObject: {// active object this: {} //this},], // scopeChain}Copy the code

// If you are not interested, you can skip it. Maybe you’ll see it in other places that are different from mine, where they write create variable objects. Below I say I have to think!

A Variable Object is a Variable Object. A Variable Object is a Variable Object.

Based on the analysis, I came to my conclusion: to understand variable objects as mutable objects. Mutable objects are objects in the execution environment that hold all variables and functions defined in the environment. The active object, created in the function execution environment, not only holds the variables and functions defined in the function execution environment, but also has a unique arguments attribute. Therefore, active objects can also be called variable objects (mutable objects).

So, a lot of things make sense. For example (the following are from JavaScript Advanced Programming Version 3, sections 4.2 and 7.2) :

If the environment is a function, consider its activation object as a variable object. Active objects start out with just one variable, the Arguments object (which does not exist in the global environment).

When a function is called, an execution context and the corresponding chain of scopes are created. Arguments and the values of other named arguments are then used to initialize the function’s live object.

Each execution environment has an object that represents a variable — a variable object.

After that, an active object (used here as a variable object) is created and pushed to the front of the execution environment scope chain. For the context in which compare() is executed in this example, the scope chain contains two variable objects: a local active object and a global variable object.

If you are interested, you can go to see what this book says. If you have different ideas, you can leave a message. Let’s have a good discussion.

(1) Establishment stage

1. Establish active Object (AO)

A. Argumentargumentargumentargumentargumentargumentargumentargumentargumentobject

B. Check function declarations in the current environment (using function declarations). Each time a function declaration is found, a property is created under the active object with the function name. The value of the property is a reference to the function’s memory address. If the function name already exists under the active object, the new function reference will be overwritten.

C. Check variable declarations in the current context (using var declarations). Each time a variable declaration is found, a property is created under the active object with the variable name and the value of the property is undefined. If the property name already exists, the new declaration is ignored.

function test() {function a() {}; var b; }test(a);Copy the code

The active object of the test function:

testAO: {    //testArguments: {... }; a:function() {}; b:undefined }Copy the code

In JavaScript, a scope is the accessible scope of a variable or function. That is, a scope controls the visibility and life cycle of a variable or function

More on scope in a later section.

What is the root cause of the variable promotion problem is in the establishment phase.

console.log(A);

function A() {}; console.log(B); var A = 666; var B = 566; console.log(A); console.log(B); //function A
//undefined
//666
//566
Copy the code

So that’s the actual order

function A() {}; //var A; The name of this var declaration, A, is ignored var B = undefined; console.log(A); console.log(B); A = 666; // reassign A to B = 566; // assign console.log(A) to B; console.log(B);Copy the code

Note the third point: when using the var declaration, if the VO object already has the property, ignore the new var declaration. Var A: var A: var A: var A: var A: var A: var A: var A: var A: var A: var A: var A: var A: var A: var A: var A: var A: var A: var A: var A: var A: var A: var A: var A: var A: var A: var A: var A: var A: var A: var A: var A

The front end of a scope chain is always the active object of the function in which the currently executing code resides. The next VO mutable object (variable object) is also called AO (active object), and so on. At the very end, is the variable object of the global environment.

Note: Although the scope chain is built when a function is called, it has nothing to do with the order of the call (the order in which it enters the call stack), because it only has to do with the containment relationship (the nested relationship between functions that contain functions). More on scopes and closures later.

Note: The scope chain is one-way, so those inside a function can access external and global variables, functions, but conversely, those outside a function cannot access internal variables, functions.

Therefore, the reference of this is determined during the execution of the function.

Later sections will elaborate on the problem this refers to.


(2) The implementation stage

1, variable assignment according to the code order, encountered variable assignment, to the corresponding variable assignment.

function getColor(){
    console.log(color);
    
    var color;
    console.log(color);
    
    color = "red";
    console.log(color);
}
getColor();
//undefined
//undefined
//"red";
Copy the code

3. Execute other code.

When the function completes execution, the local active object is destroyed (i.e., local variables, functions, arguments, etc.), and only the global scope (the variable object of the global execution environment) is stored in the scope chain.

This is very important for understanding closures, and I’ll do a closure article later, so stay tuned!