Browser first loading by < script > tag in sequence segmentation of js code block, after the completion of the loading js code block, immediately into the following three stages, and then in order to find a code block, continue to perform the following three stages, both external script files (not the asynchronous loading) and internal scripting code block, is the same principle, And they’re all in the same global scope.

There are three stages in the execution of the JS engine thread:

  • Syntax analysis
  • Precompilation stage
  • Execution phase

I. Grammatical analysis

Analyze whether the syntax of the JS script code block is correct, if incorrect, throw a SyntaxError to stop the execution of the JS code block, and then continue to find and load the next code block; If the syntax is correct, the precompilation phase is entered.

The next phase of code execution will not be checked for syntax. Parsing checks the syntax when the code block is loaded.

Ii. Pre-compilation stage

1. The running environment of js

  • Global environment (after loading the JS code, enter the code precompilation to enter the global environment)

  • Function environment (when a function call is executed, it enters the function environment. The function environment is different for different functions.)

  • Eval (not recommended, security, performance, etc.)

Each entry into a different Execution environment will create a corresponding Execution Context, so in a SECTION of JS program will generally create multiple Execution Context, the JS engine will stack these Execution Context processing, forming a function call stack. The bottom of the stack is always the Global Execution Context, and the top is always the current Execution Context.

2. Function call stack/execution stack

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

When JS code is first run, a global execution context is created and pushed into the current execution stack. Each time a function call occurs, the engine creates a new function execution context for that function and pushes it to the top of the current execution stack.

When the top function is completed, its corresponding function execution context will Pop out of the execution stack, and the control of the context will be moved to the next execution context of the current execution stack.

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 ContextCopy the code

3. Create a context

The execution context can be understood as the current execution environment and corresponding to the execution environment, which can be classified into global execution context and function execution context as mentioned above. Create a trilogy of execution contexts:

  • Create a Variable Object

  • Create Scope Chain

  • Make sure this points to

3.1 Creating variable objects

  • Create arguments objects: Check the arguments in the current context and set up the properties and values of the object. This is only done in the function environment (non-arrow functions), not in the global environment

  • Check the current context function declaration: according to the code sequence search, will find the function declaration in advance, if the current context variable object is not the function name attribute, the function name in the variable object to establish an attribute, the attribute value is to point to the function in the heap memory address references, if present, will be covered by the new reference.

  • Check the variable declaration of the current context: search in code order, declare the found variable in advance, if the variable object of the current context does not have the variable name attribute, then establish an attribute in the variable object with the variable name, the attribute value is undefined; If it exists, the variable declaration is ignored

Function declaration advance and variable declaration promotion occur in the creation of variable objects, and function declaration takes precedence over variable declaration. See below to see how function and variable declarations are advanced.

The creation of the variable object occurs in the pre-compilation stage, but it has not entered the execution stage. The variable object is not accessible, because the variable attribute in the variable object has not been assigned and the value is still undefined. Only when the variable attribute in the variable object is assigned in the execution stage, A Variable Object can be accessed only after it is turned into an Active Object. This process is called the VO – > AO procedure.

3.2 Establish scope chain

Commonly understood, the scope chain consists of the variable objects of the current execution environment (before the execution stage) and a series of active objects of the upper environment, which ensures the orderly access of the current execution environment to the variables and functions that meet the access permissions.

This can be easily understood with an example:

var num = 30;

function test() {
    var a = 10;

    function innerTest() {
        var b = 20;

        return a + b
    }

    innerTest()
}

test(a)Copy the code

In the example above, when the innerTest function is called, the innerTest function environment is entered. The global execution context and the test function execution context have entered the execution phase. The innerTest function execution context creates variable objects during the precompilation phase, so their active and variable objects are AO(global), AO(test), and VO(innerTest), respectively. The innerTest’s scope chain consists of a variable object from the current execution environment (before execution) and a series of active objects from the upper environment, as follows:

InnerTestEC = {// variable object VO: {b: undefined}, // scopeChain: [VO(innerTest), AO(test), AO(global)], //this points to this: window}Copy the code

To further understand, creating a scope chain is creating a lexical environment, which has two components:

  • Context record: The actual location where variable and function declarations are stored
  • References to the external environment: Access to its external lexical environment

The lexical environment type pseudocode is as follows:

// GlobalExectionContext = {// global execution context LexicalEnvironment: {// lexical EnvironmentRecord: {// EnvironmentRecord Type:"Object", // global environment // identifiers are bound here outer: <null> // reference to external environment}} // Second type: function environment FunctionExectionContext = {// Function execution context LexicalEnvironment: // EnvironmentRecord: {// EnvironmentRecord Type:"Declarative"// function environment // identifiers are bound here // references to the outer environment outer: <Global or outerfunction environment reference>  
  }  
}Copy the code

When you create a variable object, you create a variable environment, which is also a lexical environment. In ES6, the difference between lexical and variable environments is that the former is used to store function declarations and variable (let and const) bindings, while the latter is only used to store variable (var) bindings.

As examples:

let a = 20;  
const b = 30;  
var c;

function multiply(e, f) {  
 var g = 20;  
 return e * f * g;  
}

c = multiply(20, 30);Copy the code

The execution context is shown below

GlobalExectionContext = {

  ThisBinding: <Global Object>,

  LexicalEnvironment: {  
    EnvironmentRecord: {  
      Type: "Object"A: < initialized >, b: < uninitialized >, multiply: < func >} outer: <null>}, VariableEnvironment: { EnvironmentRecord: { Type:"Object"C: undefined,} outer: <null>}} FunctionExectionContext = {ThisBinding: <Global Object>, LexicalEnvironment: { EnvironmentRecord: { Type:"Declarative"Arguments: {0:20, 1:30, length: 2},}, outer: <GlobalLexicalEnvironment>}, VariableEnvironment: { EnvironmentRecord: { Type:"Declarative"G: undefined, outer: <GlobalLexicalEnvironment>}}Copy the code

Specific reasons for variable promotion: During the creation phase, function declarations are stored in the environment and variables are set to undefined (in the case of var) or remain uninitialized (in the case of let and const). So this is why it is possible to access variables defined by var (albeit undefined) before the declaration, but if you access variables defined by let and const before the declaration, you will be prompted with reference errors. At this time, let and const are in the uninitialized state and cannot be used. They can only be accessed after the Variable Object is converted into an Active Object after the Variable attribute is assigned to the Variable Object in the execution phase.

This article makes a good point about function declarations and variable declarations: github.com/yygmind/blo…

Another understanding of closures, for example:

function foo() {
    var num = 20;

    function bar() {
        var result = num + 20;

        return result
    }

    bar()
}

foo()Copy the code

Browser analysis is as follows:

Chrome understands that the closure is foo, so how to define a closure by browser standards can be summarized in three points:

  • Define a new function inside a function

  • The new function accesses the local variables of the outer function, that is, the active object properties of the outer function environment

  • New function execution, create a new function execution context, the outer function is the closure

3.3 this point

More complex, behind a special article to arrange.

Iii. Implementation phase

1. Web threads

Only the JS engine thread is always executing the JS script program, and the other three threads are only responsible for pushing the processing functions that meet the trigger conditions into the event queue, waiting for the JS engine thread to execute, not participating in code parsing and execution.

  • JS engine thread: Also known as the JS kernel, responsible for parsing the main thread that executes a Javascript script (e.g. V8 engine)

  • Event-triggered thread: belongs to the browser kernel process and is not controlled by the JS engine thread. It is mainly used to control events (such as mouse, keyboard and other events). When the event is triggered, the event triggering thread will push the event processing function into the event queue and wait for the JS engine thread to execute

  • Timer trigger thread: mainly controls timer setInterval and delay setTimeout, which are used for timer timing. When the timer is finished and trigger conditions of timer are met, the timer processing function is pushed into the event queue and waits for the JS engine thread to execute. Note: In the HTML standard, W3C states that setTimeout below 4ms is counted as 4ms.

  • HTTP asynchronous request thread: a thread opened by the browser after an XMLHttpRequest connection to monitor changes in the readyState state. If the callback function of the state is set, the state handler is pushed to the event queue, waiting for the JS engine thread to execute. Note: Browsers have a limit on the number of concurrent connections that can pass through a domain name. Chrome and Firefox have a limit of 6 connections and Ie8 has a limit of 10 connections.

2. Macro task

Macro tasks can be divided into synchronous tasks and asynchronous tasks:

  • Synchronous tasks refer to tasks that are executed sequentially on the main thread of the JS engine. Only after the execution of the previous task is completed, the next task can be executed, forming an execution stack (function call stack).

  • Asynchronous task refers to that the asynchronous task is not directly entered into the main thread of THE JS engine, but when the trigger condition is met, the relevant thread pushes the asynchronous task to the task queue, waits for the completion of the task on the main thread of the JS engine, and reads the executed task when idle, such as asynchronous Ajax, DOM events, setTimeout, etc.

To understand the execution sequence of synchronous and asynchronous tasks in macro tasks, it is equivalent to understanding the JS asynchronous execution mechanism – Event Loop.

3. Event loops

The event cycle can be understood as consisting of three parts:

  • Main thread execution stack

  • An asynchronous task is waiting to be triggered

  • Task queue

Task queue is the data structure of the queue to manage event tasks. It is characterized by first in, first out, last in, last out.

SetTimeout and setInterval

  • SetTimeout is to push the event to the task queue at the specified time. Only when the setTimeout event in the task queue is executed by the main thread, it will continue to push the event to the task queue at the specified time. Then the execution of the setTimeout event must take longer than the specified time. The difference depends on the code execution time

  • SetInterval pushes an event into the task queue at precise intervals every time, regardless of whether the previous setInterval event has been executed. Therefore, it is possible that setInterval event tasks may accumulate, leading to the repeated and consecutive execution of setInterval codes, affecting page performance.

4. The task

Microtasks are a type of task that occurs in ES6 and Node environments. Without es6 and Node environments, it is sufficient to understand the execution process of macro task event loops, but in ES6 and Node environments, it is necessary to understand the execution sequence of microtasks. The API of micro-task mainly includes Promise, process.nexttick

Example Understanding:

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});

console.log('script end');Copy the code

The execution process is as follows:

  • After the code block has been parsed and precompiled, it enters the execution phase, when the main thread of the JS engine executes to console.log(‘script start’); , the main thread of the JS engine considers this task to be a synchronization task, so it immediately executes the output script start, and then continues to execute down.

  • SetTimeout (function() {console.log(‘setTimeout’); }, 0); , the main thread of the JS engine considers setTimeout as an API of asynchronous task, so it applies to the browser kernel process to start the timer thread for timing and control of the setTimeout task. As W3C stipulated in HTML standard that setTimeout interval below 4ms is 4ms, when the timer thread reaches 4ms, the callback handler is pushed into the task queue to wait for the main thread to execute, and then the JS engine main thread continues to execute

  • Resolve ().then(function() {console.log(‘promise1’); }).then(function() { console.log(‘promise2’); }); The main thread of the JS engine considers the Promise to be a microtask, which divides the task into microtasks waiting to be executed

  • The main thread of the JS engine executes to console.log(‘script end’); The main thread of the JS engine considers this task to be a synchronous task, so it immediately executes the output script end

  • When the macro task on the main thread completes, it starts to detect the existence of an executable microtask. If a Promise microtask is detected, execute it immediately and output promisE1 and promisE2

  • After the execution of the microtask, the main thread begins to read the event task setTimeout in the task queue, push the main thread to form a new macro task, and then execute it in the main thread and output setTimeout

The final output is:

script start
script end
promise1
promise2
setTimeoutCopy the code

Article Reference:

Github.com/yygmind/blo…

Heyingye. Making. IO / 2018/03/19 /…

Heyingye. Making. IO / 2018/03/26 /…

Github.com/yygmind/blo…