In-depth understanding of JavaScript execution context and execution stack

  Front end, html, javascript, node.js, Programmer

Preface

If you are a JavaScript developer or want to be a JavaScript developer, then you must know the internal execution mechanism of the JavaScript program. Execution context and execution stack are one of the key concepts in JavaScript and one of the difficulties in JavaScript. Understanding the execution context and the execution stack is also helpful to understand other JavaScript concepts such as promotion mechanism, scope, closure, etc. This article tries to introduce these concepts in an easy-to-understand way.

If you want to read more excellent articles, please stamp them fiercely.GitHub blog

I. Execution Context

1. What is the execution context

In short, the execution context is the abstract concept of the environment in which the current JavaScript code is parsed and executed. Any code running in JavaScript is run in the execution context.

2. Type of execution context

There are three types of execution contexts:

  • Global Execution Context: This is the default and most basic execution context. Code that is not in any function is in the global execution context. It does two things: 1. Create a global object, which is the window object in the browser. 2. Point this pointer to this global object. Only one global execution context can exist in a program.
  • Function Execution Context: Each time a function is called, a new execution context is created for the function. Each function has its own execution context, but it is only created when the function is called. Any number of function execution contexts can exist in a program. Every time a new execution con text is created, it will execute a series of steps in a specific order. The specific process will be discussed later in this article.
  • Eval Function Execution Context: The code running in the eval function also obtains its own execution context, but it is not discussed here because Javascript developers do not commonly use eval functions.

II. Life Cycle of Execution Context

The life cycle of an execution context consists of three phases:Creation phase → Implementation phase → Recovery phaseThis article focuses on the creation phase.

1. Creation phase

When a function is called but does not execute any of its internal code, it does the following three things:

  • Create a variable object: First initialize the arguments of the function, promote the function declaration and variable declaration. As will be explained in detail below.
  • Create Scope Chain: In the creation phase of the runtime context, scope chain is created after the variable object. Scope chain itself contains variable objects. Scope chain is used to parse variables. When asked to parse a variable, JavaScript always starts from the innermost layer of code nesting. If the innermost layer does not find a variable, it will jump to the parent scope of the previous layer to find the variable until it is found.
  • Determine the direction of this: including a variety of situations, which will be described in detail below

Before a JS script is executed, the code must be parsed (so JS is the scripting language for interpreting execution). When parsing, a global execution context environment will be created, and the variables and function declarations to be executed in the code will be taken out first. Variables are temporarily assigned to undefined first, while functions are declared available first. This step is completed, and then the formal execution of the procedure will begin.

In addition, before a function is executed, a function execution context environment will be created, which is similar to the global context, but there will be more this arguments and function parameters in the function execution context.

2. Implementation phase

Execute variable assignment, code execution

3. Recovery phase

The execution context is pushed out of the stack to wait for the virtual machine to reclaim the execution context

III. Details of Variable Promotion and this Pointing

1. Variable declaration promotion

Most programming languages declare variables before using them, but in JS, things are different:

console.log(a)// undefined
 var a = 10

The above code is normally output.undefinedInstead of reporting an errorUncaught ReferenceError: a is not definedThis is because declaration hoisting is equivalent to the following code:

var a;  //Declare that the default value is undefined "Preparation"
 console.log(a);
 a=10;  //assignment

2. Function declaration promotion

We all know that there are two ways to create a function, one is through function declarationfunction foo(){}
The other is through function expressionvar foo = function(){}What is the difference between the two in function promotion?

console.log(f1) // function f1(){}
 Function f1() {} // function declaration
 console.log(f2) // undefined
 Var f2 = function() {} // function expression

Next, we will illustrate this problem through an example:

function test() {
 foo();  // Uncaught TypeError "foo is not a function"
 bar();   // "this will run!"
 var foo = function () { // function expression assigned to local variable 'foo'
 alert("this won't run!  ");
 }
 function bar() { // function declaration, given the name 'bar'
 alert("this will run!"  );
 }
 }
 test();

In the above example, foo () was called incorrectly, while bar was able to call normally.

As we said earlier, both variables and functions will rise, and function expressions will be encountered.var foo = function(){}When, the first will bevar fooIt rises to the top of the function body, but the value of foo at this time is undefined, so it is executed.foo()Report an error.

For functionsbar(), is to promote the whole function, sobar()In order to be able to carry out smoothly.

One detail must be noted:When a function and a variable have the same name and are both promoted, the function declaration has a higher priority, so the variable declaration will be overwritten by the function declaration, but can be reassigned.

alert(a);  //Output: function a(){ alert ('I am a function')}
 Function a(){ alert ('I am a function')}//
 Var a =' i am a variable';
 alert(a);  //Output:' I am a variable'

Function declaration has higher priority than var declaration, which means that when two variables with the same name are declared by function and var simultaneously, function declaration will override var declaration.

This code is equivalent to:

Function a(){alert ('I am a function')}
 var a;  //hoisting
 alert(a);  //Output: function a(){ alert ('I am a function')}
 A =' I am a variable';  //assignment
 alert(a);  //Output:' I am a variable'

Finally, let’s look at an example of a complex point:

function test(arg){
 // 1. formal parameter arg is "hi"
 // 2. arg is function at this time because function declaration has higher priority than variable declaration
 console.log(arg);
 var arg = 'hello';  // 3.var arg variable declaration ignored, arg = 'hello' executed
 function arg(){
 console.log('hello world')
 }
 console.log(arg);
 }
 test('hi');
 /* Output:
 function arg(){
 console.log('hello world')
 }
 hello
 */

This is because when the function is executed, a new private scope will be formed first, and then the following steps will be followed in sequence:

  • If there is a tangible parameter, assign a value to the formal parameter first.
  • For pre-interpretation in private scope, the priority of function declaration is higher than that of variable declaration, and finally the latter will be covered by the former.But can be reassigned
  • Code in private scope executes from top to bottom

3. Determine the direction of this

First understand a very important concept-The value of this can only be confirmed when it is executed, and cannot be confirmed when it is defined!Why? Because this is part of the execution context, and the execution context needs to be determined before the code is executed, not when it is defined. Look at the following example:

//Case 1
 function foo() {
 console.log(this.a) //1
 }
 var a = 1
 foo()
 
 //Case 2
 function fn(){
 console.log(this);
 }
 var obj={fn:fn};
 obj.fn();  //this->obj
 
 //Case 3
 function CreateJsPerson(name,age){
 //this is an instance p1 of the current class
 this.name=name;  //=>p1.name=name
 this.age=age;  //=>p1.age=age
 }
 Varp1 = newcreatejsohn ("yin huazhi", 48);
 
 //Case 4
 function add(c, d){
 return this.a + this.b + c + d;
 }
 var o = {a:1, b:3};
 add.call(o, 5, 7);  // 1 + 3 + 5 + 7 = 16
 add.apply(o, [10, 20]);  // 1 + 3 + 10 + 20 = 34
 
 //Case 5
 < button id="btn1 "> arrow function this</button >
 <script type="text/javascript">
 let btn1 = document.getElementById('btn1');
 let obj = {
 name: 'kobe',
 age: 39,
 getName: function () {
 btn1.onclick = () => {
 console.log(this);  //obj
 };
 }
 };
 obj.getName();
 </script>

Next, let’s explain the above situations one by one.

  • For calling foo directly, this must be window no matter where the foo function is placed.
  • For obj.foo (), we just need to remember that whoever calls the function is this, so this in the foo function is the obj object in this scenario
  • In constructor mode, this in this.xxx=xxx, which appears in the class (in the function body), is an instance of the current class
  • Call, apply, and bind:this is the first parameter
  • Arrow function this points to: the arrow function does not have its own this, see if there is a function on its outer layer. if there is, this of the outer layer function is this of the inner arrow function, if not, this is window.

IV. Execution Context Stack

When there are more functions, there are multiple function execution contexts. Every time a function is called, a new execution context is created. How do you manage so many execution contexts created?

The JavaScript engine creates an execution context stack to manage the execution context.The execution context stack can be considered as a stack structure for storing function calls and follows the principle of first in and second out..

From the above flowchart, we need to remember several key points:

  • JavaScript is executed on a single thread, and all code is queued for execution.
  • At first, when the browser executes the global code, it first creates the global execution context and pushes it to the top of the execution stack.
  • Every time a function is executed, the execution context of the function is created and pushed to the top of the execution stack. After the execution of the current function is complet ed, the execution context of the current function is pushed out of the stack and waits for garbage collection.
  • The browser’s JS execution engine always accesses the execution context at the top of the stack.
  • There is only one global context, which is pushed out of the stack when the browser is closed.

Let’s look at another example:

var color = 'blue';
 function changeColor() {
 var anotherColor = 'red';
 function swapColors() {
 var tempColor = anotherColor;
 anotherColor = color;
 color = tempColor;
 }
 swapColors();
 }
 changeColor();

The above code runs as follows:

  • When the above code is loaded in the browser, the JavaScript engine creates a global execution context and pushes it onto the current execution stack
  • When the changeColor function is called, the internal code of the changeColor function has not been executed at this time. The js execution engine immediately creates an execution context (EC for short) of changeColor, and then pushes the execution context into the execution stack (ECStack for short).
  • During the execution of the changeColor function, the swapColors function is called. Similarly, before the swapColors function is executed, an execution context of swapColors is created and pushed into the execution stack.
  • The execution of swapColors function is completed, and the execution context of swapColors function is pushed out of the stack and destroyed.
  • The execution of the changeColor function is completed, and the execution context of the changeColor function is pushed out of the stack and destroyed.

To recommend a useful BUG monitoring toolFundebug, welcome to try free!

Welcome to pay attention to the public number:Front end craftsmanWe witness your growth together! If you feel fruitful, please give me a reward to encourage me to output more high-quality open source content.

Reference article