preface

We have all heard of JS engine this thing, but I believe that for most people, this is a black box, to its understanding is not deep, anyway I will give JS code to you, you clever point, execute well and then tell me the result. So for the JS engine this black box, this article will not be too deep, just can let you have a basic understanding of the JS engine this thing.

What is a JS engine?

A JavaScript engine is a virtual machine that specializes in JavaScript scripts and is usually attached to a web browser.

The JavaScript engine, or JavaScript virtual machine, can be simply understood as a translator that translates JavaScript from a programming language that humans understand into a machine language that machines understand.

SpiderMonkey, Rhino, TraceMonkey, JaegerMonkey, JavaScriptCore, V8, etc. V8 is an open source project developed by Google. It is the most widely used JavaScript VIRTUAL machine. There are more than 2.5 billion Android devices around the world, and all of them use Chrome. Mostly on v8s.

The main function of V8 is to compile and execute it in combination with the features and nature of the JavaScript language. Understanding how V8 actually executes JavaScript code will be easier if we understand some of the basic features and design ideas of the language.

Language type

Every programming language has built-in data types that differ in many ways and are used in very different ways. Data types can be divided into static and dynamic languages based on whether they need to be validated before use, and weakly and strongly typed languages based on whether implicit type conversions are supported.

  • A static language is one that needs to confirm its variable data type before it can be used
  • Languages that need to check variable data types at run time are called dynamic languages
  • Languages that support implicit conversion of variables are called weakly typed languages
  • Languages that do not support implicit typing of variables are called strongly typed languages

For example, C# and Java are strongly typed static languages. C and C++ are weakly typed static languages; Python and Ruby are strongly typed dynamic languages. JavaScript and PHP are weakly typed dynamic languages.

JavaScript is a weakly typed, dynamic language:

  • Weakly typed, which means there is no need to tell the JS engine what data type a variable is, it will check it out as it runs the code, and this layer of computation also means a bit more performance cost than strongly typed languages.
  • Dynamic, which means you can use a variable to hold different types of data.

Compiler and interpreter

Machines cannot directly understand the code we write, so we have to translate the code into machine language that the machine can understand before we execute the program. Then according to the execution process of language, language can be divided into compiled language and interpreted language.

Compiled language

Compiled languages need to be compiled by the compiler before the program is executed, and after compilation, the machine-readable binary file is kept directly, so that the binary file can be run each time the program is run without the need for recompilation. C/C++, GO, etc are compiled languages.

  • In a compiled language, the compiler first conducts lexical analysis of the source code, then syntactic analysis to generate an abstract syntax tree (AST), then optimizes the code, and finally generates machine code that the processor can understand.
  • If the compilation succeeds, an executable file is generated.
  • But if a syntax or other error occurs during compilation, the compiler will throw an exception and the resulting binary will not be generated successfully.

Interpretive language

A program written in an interpreted language needs to be dynamically interpreted and executed by an interpreter each time it is run. Python, JavaScript, etc., are interpreted languages.

  • In the process of interpreting interpreted languages, the interpreter also conducts lexical analysis and syntax analysis of source code to generate abstract syntax tree (AST). However, it generates bytecode based on the abstract syntax tree, and finally executes programs and outputs results according to bytecode.

Just-in-time compilation (JIT)

Before V8, all JavaScript virtual machines used interpretation execution, which was a major reason for slow JavaScript execution. V8 was the first to introduce just-in-time (JIT) dual-wheel drive design, a tradeoff strategy in which a mix of compile execution and interpreted execution resulted in a significant increase in JavaScript execution speed.

Instant compiler (JIT) is a kind of using bytecode technology with the interpreter and compiler implementation code, inside the V8, the interpreter in interpreting execute bytecode at the same time, the code will be carried out in the collection of information, when it is found that some part of the code becomes hot (a piece of code is executed multiple times), the compiler will put hot bytecode into machine code, And the converted machine code is saved for future use.

V8 The process of executing a piece of JavaScript code

The process of executing a piece of code is shown in the following flowchart:

  1. Convert the source code into an abstract syntax tree (AST) and generate an execution context

Generating an abstract syntax tree (AST) goes through two stages (word segmentation and parsing) :

  • The first stage isWord segmentation (tokenize), also known asLexical analysisIts function is to break down the line of source code into individual piecestoken. The so-calledtoken, refers to the smallest single character or string that is syntactically impossible to divide.
  • The second stage isResolution (parse), also known asSyntax analysis, its role is to generate the previous steptokenData is converted to AST according to the syntax rules. If the source code is syntactically correct, this step is done smoothly. But if there is a syntax error in the source code, this step terminates and a “syntax error” is thrown.

Babel converts ES6 code into ES5 code by first converting ES6 source code into an AST and then converting the AST of ES6 syntax into an AST of ES5 syntax. Finally, ES5 AST is used to generate JavaScript source code.

  1. The interpreter (Ignition) generates bytecode from the Abstract syntax tree (AST) and interprets and executes bytecode.

Bytecode is a type of code between AST and machine code. But regardless of a particular type of machine code, bytecode needs to be translated into machine code by the interpreter before it can be executed.

Because V8 consumes a lot of memory to hold the converted machine code, the V8 team solved this problem by using bytecode to reduce the system’s memory usage.

  1. Execute the code and print the results

Typically, if you have a piece of bytecode being executed for the first time, the interpreter (Ignition) interprets the execution line by line. In addition to generating the bytecode, the interpreter (Ignition) interprets the execution of the bytecode.

When the interpreter Ignition executes bytecode, if it finds hot code (a piece of code that has been executed multiple times), TurboFan compiles the hot bytecode into efficient machine code, and when it executes the optimized code again, It only needs to execute the compiled machine code, which greatly improves the efficiency of code execution.

The reason V8 takes longer to execute and is more efficient is that more code becomes hot code and is converted to machine code for execution.

conclusion

  • The JS engine can translate the high-level programming language JavaScript into a machine language that the machine can understand. The most representative JS engine is V8.
  • JavaScript is a weakly typed dynamic language, which requires an interpreter to dynamically interpret and execute JavaScript.
  • V8 executes a piece of JavaScript code as follows:
    • Convert the source code into an abstract syntax tree (AST) and generate an execution context
    • The interpreter generates bytecode from the abstract syntax tree (AST) and interprets the execution of the bytecode
    • Execute the code and print the results