In-depth study of JS series is the witness of my phased growth. I hope to comb relevant JS knowledge more rigorously and objectively through the form of articles. I also hope to help more friends of front-end development to solve problems and look forward to our common progress.

If you think this series is good, welcome to like, comment, forward, your support is the biggest power OF my persistence.


The opening

As a JavaScript developer, if you are asked about the order in which your JavaScript code is executed, do you have an intuitive impression that JavaScript is executed sequentially, but is that really the case?

Let’s start with two small examples:

var foo = function () {
  console.log('foo1');
}

foo();  // foo1

var foo = function () {
  console.log('foo2');
}

foo(); // foo2
Copy the code
function foo() {
  console.log('foo1');
}

foo();  // foo2

function foo() {
  console.log('foo2');
}

foo(); // foo2
Copy the code

As anyone who has brushed through the interview questions knows:

Rather than analyzing and executing the program line by line, the JavaScript engine analyzes the execution section by section, and as it executes a piece of code, it does a preparatory work.

For example, variable promotions like function promotions that we’re familiar with in JavaScript are done in this preparation phase.

In this article, we will take an in-depth study. How is this paragraph divided?

What kind of code does a JavaScript engine “prepare” for? To answer this question we introduce the concept of execution context.

Execution context

If you have done reading comprehension in primary school, you must have seen the question: Explain the sentence with context. The context here may be the paragraph in which the sentence is located, or it may be the paragraph next to the paragraph in which the sentence is located. In fact, what is described here is the context and scope of a sentence, which can be defined as follows by analogy to the program:

An execution context is an abstraction of the context in which the current JavaScript code is being parsed and executed.

The type of execution context

There are three types of execution contexts, sometimes called executable code

  • Global execution context: There is only one, the global object in the browserwindowObject,thisPoint to this global object.
  • Function execution contexts: There are an infinite number of them, which are created only when a function is called, and a new execution context is created each time a function is called.
  • EvalFunction execution context: refers to the execution contextevalCode in a function, rarely used and not recommended.

For example, when a function is executed, there is preparation, which is, to use a more technical term, called an “execution context.”

Execution stack

The question then arises, how do we manage the creation of so many execution contexts when we write so many functions? So the JavaScript engine creates an Execution Context stack to manage the Execution context.

Here we can simply think of ECStack as an array, like this:

ECStack = [];
Copy the code

The execution stack, also known as the call stack, has a LIFO(Last in First Out) structure for storing all execution contexts created during code execution.

  • When JavaScript code is first run, a global execution context is created and pushed onto the current execution stack. Each time a function call occurs, the engine creates a new function execution context for that function and pushes the top of the current execution stack.
  • When the function at the top of the stack completes, its corresponding function execution context pops out of the execution stack, and control of the context moves to the next execution context on the current execution stack.

Let’s look at some code to understand this process:

var a = 'Hello World! ';

function first() {  
  console.log('Inside first function');  
  second();  
  console.log('Again inside first function');  
}

function second() {  
  console.log('Inside second function');  
}

first();  
console.log('Inside Global Execution Context');

// Inside first function
// Inside second function
// Again inside first function
// Inside Global Execution Context
Copy the code
  • When the above code loads in the browser, the JavaScript engine creates a global execution context and pushes it onto the current execution stack. When the first() function call is encountered, the JavaScript engine creates a new execution context for the function and pushes it to the top of the current execution stack.
  • When second() is called from within the first() function, the JavaScript engine creates a new execution context for second() and pushes it to the top of the current stack. When second() completes execution, its execution context pops off the current stack. And control the flow to the next execution context, that of the first() function.
  • When first() completes, its execution context pops out of the stack, and the control flow reaches the global execution context. Once all the code has been executed, the JavaScript engine removes the global execution context from the current stack.

The following diagram illustrates the above execution process more clearly

Let’s look at two thought questions

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f();
}
checkscope();
Copy the code
var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}
checkscope()();
Copy the code

Two pieces of code execute the same, but what are the differences between them?

The answer is that the execution context stack changes differently.

Let’s simulate the first code:

ECStack.push(<checkscope> functionContext);
ECStack.push(<f> functionContext);
ECStack.pop();
ECStack.pop();
Copy the code

Let’s simulate the second code:

ECStack.push(<checkscope> functionContext);
ECStack.pop();
ECStack.push(<f> functionContext);
ECStack.pop();
Copy the code

checkscope()(); Here is some explanation of how this function is executed

// checkscope()(
var f = checkscope();
f();
Copy the code

The checkscope function returns a function name equivalent to:

ECStack.push(<checkscope> functionContext);
ECStack.pop();
Copy the code

The return function, then executed, is equivalent to:

ECStack.push(<f> functionContext);
ECStack.pop();
Copy the code

In order to explain the difference in the execution of the two functions in more detail, we need to explore what is involved in the execution context, and we need to learn more about variable objects.

Reference links:

“Understanding execution Context and Execution Stack in JavaScript”

JavaScript Deep Execution Context Stack

Learn more about the JavaScript catalog

  • #1 [In-depth study of JS — Prototype and Prototype Chain]
  • #2: Lexical scope and dynamic scope
  • #3 [In-depth study of JS — implement Yamashita Text stack]
  • #4 【 In-depth study of JS – variable objects 】
  • #5 [In-depth study of JS — scope chain]
  • #6 [In-depth study of JS — This point in real Development Scenarios]
  • #7: The execution context of JS
  • #8: Js closures
  • #9 — Parameter passing by value

Welcome to add my personal wechat to discuss technology and personal growth.