Abstract: Understand the principle of JS execution.

  • How does JavaScript work?
  • Author: hengg

FundebugReproduced with authorization, copyright belongs to the original author.

What is JavaScript?

Let’s confirm the definition of JavaScript: JavaScript is an interpreted, dynamic language.

Interpreted languages exist relative to compiled languages. Source code is not directly compiled into object code, but converted into intermediate code, which is then interpreted and run by the interpreter.

The major programming languages are compiled (e.g. C++), interpreted (e.g. JavaScript), and semi-interpreted, semi-compiled (e.g. Java).

How does the code work?

First let’s take a look at how the code works.

We know that the code is executed by the CPU, and the CURRENT CPU can’t directly execute things like if… Else or something like that, which can only execute binary instructions. But binary instructions are unfriendly to humans: it’s hard to quickly and accurately tell what 1000010010101001 represents. So scientists invented assembly language.

Assembly language

Assembly language is essentially a mnemonic for binary instructions.

Assuming that 10101010 represents a memory read operation, the memory address is 10101111, and the register address is 11111010, then the complete operation 101010101010101111111010 represents reading the value of a memory address and loading it into a register. Assembly language does not change this operation. It is simply a mapping of binary instructions:

LD: 10101010 ID :10101111 R:11111010Copy the code

The above instructions can then be expressed as LD ID R, greatly improving the readability of the code.

However, this is not friendly enough. The CPU can only execute three-address expressions, which are far from the way people think and language patterns. So great scientists invented high-level languages.

A high-level language

“Code is written for people, not machines, but by the way computers can execute it.”

High-level languages are called “high-level” because they are more in tune with our thinking and reading habits. The if… Else looks a lot more comfortable than 1010101010. But the computer cannot execute the high-level language directly, so it needs to convert the high-level language into assembly language/machine instruction to execute. That process is compilation.

Does JavaScript need to compile?

JavaScript is unquestionably a high-level language, so it must be compiled to execute. But why do we call it interpretive language? How is it different from compiled, semi-interpreted, and semi-compiled languages? Let’s start with compilation.

compile

Now that we’ve seen the concept of compilation, let’s talk about platforms: a piece of C++ code will compile to an.obj file on Windows, and an.o file on Linux. This is because an executable requires system resources such as operating system apis, memory, threads, and processes in addition to code, and implementations vary from operating system to operating system. For example, we are familiar with I/O multiplexing (event-driven soul), which is implemented in the IOCP scheme on Windows and the Epoll scheme on Linux. So compiled languages need to be compiled, or even written, separately for different platforms, and the resulting executable files have different formats.

cross-platform

Java takes this one step further by introducing bytecode to run across platforms: Java files compile to.class files (this is bytecode files, an intermediate form of object code) on any operating system. Java then provides different Java virtual machines to interpret and execute bytecode files for different systems. Interpreted execution does not generate object code, but it is eventually translated into assembly/binary instructions for the computer to execute.

If we were to write a simple operating system entirely on our own, would it run Java? Obviously not, because there is no JVM for this system. So cross-platform for Java, cross-platform for any other language, is limited.

The advantage of Java’s use of half-interpret and half-compile is that it greatly improves the development efficiency, but correspondingly reduces the code execution efficiency, after all, virtual machines have a performance penalty.

Explain to perform

JavaScript goes a step further. It is fully interpreted execution, or just-in-time compilation. There will be no intermediate code generation, and there will be no object code generation. This process is usually handled by the host environment (e.g., browser, Node.js).

The build process

Now we confirm that even the language that explains the execution needs to be compiled. So how does the code compile? Let’s look at it briefly.

Lexical analysis

Lexical analysis breaks down statements into lexical units, known as tokens.

function square(n){
	return n*n;
}
Copy the code

This function is recognized by the lexical analyzer as function, square, (, n,), {, return,, n,*, n,} and labeled to indicate whether it is a variable or an operation.

Syntax analysis

This process converts the Token into an abstract syntax tree (AST) :

{
	type:'function',
    id:{
        type:'id'
        name:'square'
    },
    params:[
        {
            type:'id',
            name:'n'
        }
    ]
    ...
}
Copy the code

Optimization and code generation

In this step, the compiler does some optimizations, such as removing redundant operations, removing unused assignments, merging some variables, etc., and finally generating object code.

Because just-in-time compilation of a language usually happens a few microseconds before it runs, the compiler doesn’t have time to do much optimization. This is one of the reasons for the poor performance of early JavaScript compared to compiled languages. But for now, thanks to the V8 engine (node.js can use V8’s JS2C tools to translate JavaScript into C++ code, rather than bytecode or interpretive execution, as earlier JavaScript engines did), The performance gap between JavaScript and other languages is no longer enough.

Linking and loading

The object code basically doesn’t run independently. Applications generally consist of multiple parts (modules). For example, in C++, a simple output is imported into the standard library iostream:

#include <iostream>
using namespace std;
int main(a){    
    cout << "Happy Hacking! \n";    
    return 0;
}
Copy the code

The compiler needs to link together multiple pieces of object code (libraries) to produce an executable file. At this point, we have a brief overview of the compilation process. But compilation is actually a lot more complicated than that, so we won’t expand it here.

What are dynamic languages, dynamic typing?

We also know that JavaScript is a dynamic language. So what is a dynamic language?

Generally speaking, this refers to a language in which code can change its structure at run time depending on certain conditions. For example, JavaScript can introduce new functions, objects, and even code at runtime (eval). Objective-c, for example, can also modify objects at run time, but it can’t create classes on the fly, and there’s no eval method. Is Objective-C a dynamic language? So I think dynamic languages are a matter of degree, we don’t have to get too hung up on the concept, we can focus more on the application. The hot update function commonly used in APP is realized based on the feature of dynamic language.

JavaScript is a dynamically typed language. What is dynamic typing? The definition of dynamic typing is straightforward: data types are determined not at compile time, but at run time.

So what kind of language is TypeScript? It has static type checking, is it a static language? It’s really just a dialect of JavaScript. TypeScript ultimately has to be translated into JavaScript for execution (TSC), just as we use Babel to translate ES6 code into ES5. This process is not strictly compilation.

One of TypeScript’s greatest strengths is static type checking and type inference, capabilities that JavaScript sorely lacks. In fact, if we ignore the ERROR message from the IDE and run the TS code, there is still a chance that it will work.

error

We just mentioned error reporting, so let’s expand to error reporting. Errors generally fall into the following categories:

  • Compile time error
  • Link error
  • Runtime error

Does it correspond strictly to the compilation process?

Compile time error

Compile-time errors are classified as:

  • Grammar mistakes

    var str ='s ;
    Copy the code

    This is a classic syntax error, as code that does not generate an AST will report errors during the lexical analysis phase. Usually when we write code like this, the IDE will report an error. This is IDE optimization work, and related to lexical analysis.

  • Type error

    The compiler checks the types of variables and functions we declare, such as the familiar Type Error:undefined is not object in JavaScript.

Link error

An exception that occurred during the link phase. This is rare in JavaScript and common in compiled languages.

Runtime error

This is the most difficult error to detect. For example:

int divider(int a,int b){
    return a/b;
}
Copy the code

The above code in the edit compilation, link stage is no problem, also can normally generate executable files. However, using divider(1,0) in this way results in an error, which is a classic runtime error. Run-time errors are usually the result of an unrobust program.

The ten most common errors in JavaScript:

The following figure shows the Top10 JavaScript errors collected by an error processing platform, including 7 typeerrors and 1 ReferenceError:

Obviously, we can deal with all eight of these problems with TypeScript early in coding.

conclusion

Now we know how JavaScript works. But will knowing this help us write better code?

The answer is yes. Not to mention TypeScript’s ability to improve type checking and type inference, JavaScript’s scope, this, is also strongly related to the compilation process; Most of the current applets support one set of code and multiple platforms. After reading this article, you will have a general understanding of the principles behind these technologies. Happy Hacking!

By the way, I recommend it to youFundebug, very useful BUG monitoring tools ~

About Fundebug

Fundebug focuses on real-time BUG monitoring for JavaScript, wechat applets, wechat games, Alipay applets, React Native, Node.js and Java online applications. Since its official launch on November 11, 2016, Fundebug has handled over 2 billion error events in total. Its paid customers include Sunshine Insurance, Walnut Programming, Lychee FM, Zhanmen 1-on-1, Weimai, Qingtuanshe and many other brand enterprises. Welcome to try it for free!