Technology sharing: deep understanding of JavaScript Design with V8

Shuzhan Study Club 2021-06-19 06:02:57
technology sharing deep understanding javascript

JavaScript Code runs

To develop common Chrome The browser or Node give an example , our JavaScript The code is through V8 Running . but V8 How to execute the code ? When we type in const foo = {foo:'foo'} when V8 What else has been done ? The author first throws out the above questions , Let's move on .

JavaScript Storage

When the code is running , The most important premise is to have a place to store state , This is what we call stack space .

Our base types are stored on the stack , It will be recycled automatically ; Composite types are stored in the heap , adopt GC Operation for space release . This process is implicit to the user , So users have to follow JavaScript Write code according to the specifications of , If it doesn't meet the specifications , that GC You can't recycle space properly , So it will cause ML The phenomenon , More serious will cause OOM.

In order to more intuitively see the storage form of each type in memory , I created a basic type variable Foo, The compound type Bar, And a statement John, And give their state diagram in the memory stack :

1、 About GC

Through the above analysis , We mentioned that GC Invalid objects will be recycled and space released , For users , Whether it's a base type or a composite type, their declaration and release are automatic . But actually the heap recycling is manual , It's just V8 The level has already helped us achieve it , And the process is not completely free (write barrier). But this automatic process allows most developers to completely ignore its existence , obviously JavaScript It was designed on purpose

write barrier It is used to notify when the asynchronous three color marking algorithm is in progress GC At present, all operations of object graph change , To ensure the accuracy of three color marking method in asynchronous process , v8 Inserted write barrier Code .

// Called after `object.field = value`. write_barrier(object, field_offset, value) { if (color(object) == black && color(value) == white) { set_color(value, grey); marking_worklist.push(value); } }

2、JavaScript The positioning of

Have used C / C++ Students must have a deep understanding of manual memory operation and memory release , meanwhile GO and D There's also the concept of pointers . Generally speaking , If a language is located in “ System level ” Can operate memory space directly , In addition to the language mentioned above ,Rust It's also a system level language ,FileFox Of VM TraceMonkey Write in this language .

It is worth mentioning that TraceMonkey The forerunner of MoonMonkey It's the first one in the world JavaScript engine . Of course , The so-called direct operation of memory stack content , Or after some hardware mapping , Our high-level language is OS The upper , therefore OS It still gives the program the illusion of directly operating memory .

go back to JavaScript , Obviously, it's not a definition of “ System level ” Language , More upstream application level languages , Therefore, language design and application scenarios tend to hide some underlying concepts .

In addition to the positioning of language ,JavaScript It's a dynamically typed language , This means that there is a lot of runtime information at the language runtime , It records the global execution context 、 Global scope 、 Prototype chain inheritance Information, etc. , Because these features have to be done at runtime , So there's another need V8 The reason of , It also leads to V8 The role of the interpreter in .

1) About CPU

Before I introduce the interpreter , Let's take a look first CPU. current CPU Very complicated , Let's put the CPU Purity , Having a simple instruction set 、ALU、 register . When it executes code, the idea is very simple , It's a bunch of if ... else ... To determine the current instruction code , Parsing instructions .

In other words ,CPU The basic work of is to calculate and jump according to the operation code , It doesn't check that the program is correct , As long as the opcode matches, it will be executed , Naturally, it doesn't matter what data is in the content stack . Here are RISC-V Processor code snippet , You can see that it's just by judging instructions , Carry out the corresponding operation .

while(1){ iters++; if((iters % 500) == 0) write(1, which_child?"B":"A", 1); int what = rand() % 23; if(what == 1){ close(open("grindir/../a", O_CREATE|O_RDWR)); } else if(what == 2){ close(open("grindir/../grindir/../b", O_CREATE|O_RDWR)); } else if(what == 3){ unlink("grindir/../a"); } else if(what == 4){ if(chdir("grindir") != 0){ printf("grind: chdir grindir failed\n"); exit(1); } unlink("../b"); chdir("/"); } else if(what == 5){ close(fd); fd = open("/grindir/../a", O_CREATE|O_RDWR); } else if(what == 6){ close(fd); fd = open("/./grindir/./../b", O_CREATE|O_RDWR); } else if(what == 7){ write(fd, buf, sizeof(buf)); } else if(what == 8){ read(fd, buf, sizeof(buf)); } else if(what == 9){ mkdir("grindir/../a"); close(open("a/../a/./a", O_CREATE|O_RDWR)); unlink("a/a"); } else if(what == 10){ mkdir("/../b"); close(open("grindir/../b/b", O_CREATE|O_RDWR)); unlink("b/b"); } else if(what == 11){ unlink("b"); link("../grindir/./../a", "../b"); } else if(what == 12){ unlink("../grindir/../a"); link(".././b", "/grindir/../a"); } else if(what == 13){ int pid = fork(); if(pid == 0){ exit(0); } else if(pid < 0){ printf("grind: fork failed\n"); exit(1); } wait(0); } else if(what == 14){ int pid = fork(); if(pid == 0){ fork(); fork(); exit(0); } else if(pid < 0){ printf("grind: fork failed\n");

So back to V8,V8 One of the functions of the interpreter is to record the runtime state of a program , You can track memory , Variable type monitoring , To ensure the security of code execution . stay C / C++ In the language of manual operation of memory, if the memory has a small overrun, it will not necessarily cause the program to crash , But there's bound to be problems , But it's time-consuming .

Now that I have mentioned V8 Interpreter related concepts , So let's continue to expand on this , Positive cause JavaScript It's a dynamically typed language , So we need an interpreter to process the code , So early JavaScript The engine runs code very slowly , So the interpreter has a big feature , That's fast startup , Slow execution .

To improve the problem , therefore V8 Real time compilation was first introduced (JIT) The concept of , Later, other engines were introduced , So most of what's popular now JavaScript All engines have this feature . It mainly uses trade-offs , Using both interpreter and compiler .

Compilers are slow to start , Fast execution . This is how they work together : Code to AST After that, it will be handed over to the interpreter for processing , If the interpreter detects a part JavaScript The code runs more times , And it's a fixed structure , Then it will be marked as hot code and handed over to the compiler for processing , The compiler compiles that part of the code into binary machine code , And optimize , The optimized binary code is given to CPU The execution speed will be greatly improved .

At the same time, it leads to a need V8 The reason of : Because of different CPU The instruction set of is different , Therefore, in order to achieve cross platform, we must do a layer of abstraction , and V8 That's the abstraction , To break away from the machine dependency of the target code .

Talking about it here , The students must know why we need V8 as well as V8 The bottom layer is about how to execute a paragraph JavaScript Code , But in the above process, the most important thing is that we need V8 Why , So I dodged a lot V8 Compile time details .

In a nutshell ,JavaScript It's a language of application orientation , For the convenience of security , Cross platform , The control of runtime state , So we chose to set another layer on the real machine for processing , You can also call this layer VM ( virtual machine ).

V8 The build process

Let's talk about it in detail V8 How to execute JavaScript Code , According to the above V8 To improve execution efficiency , A mix of interpreted execution and compiled execution , That's what we call just in time compilation (Just In Time), At present, there are many languages using this kind of method, such as Java Of JVM,  lua The script LuaJIT wait , When we code :
foo({foo: 1});
function foo(obj) { const bar = + 1
return bar + '1' }

We can find out foo It's executable , stay JavaScript In language, we call this phenomenon variable elevation , But from another perspective , Pay attention to my address on it ? code ; The code we write is just for human beings , It's just meaningless characters for machines , Therefore, it is also called high-level language . So the final execution and the coding we wrote can not be equal , So we can't understand and execute according to our code .

But how does the machine deal with our code ? Because encoding strings is not easy for machines to operate , So we're going to turn it into AST ( Abstract syntax tree ), Using this tree like data structure , Can be very clear and effective operation of our code , Translate it into a machine language that the machine can understand .

that V8 How to deal with variable Promotion , Obviously V8 Start execution JavaScript Before code , It needs to know which variable declaration statements are available , Put it in the scope .

According to the above analysis , We can know V8 When it starts , First you need to initialize the execution environment , and V8 The main initialization operations in are :
  • initialization “ Heap space ”、“ Stack space ”

  • Initialize the global context , Including global information during execution , Variable etc.

  • Initialize global scope . Function scopes and other child scopes exist only at execution time

  • Initializes the event loop system

After initialization ,V8 Will use the parser to structure the code into AST, Let's take a look V8 Generated AST What does it look like , The coding is based on the example above .
[generating bytecode for function: foo] --- AST --- FUNC at 28 . KIND 0 . LITERAL ID 1 . SUSPEND COUNT 0 . NAME "foo" . PARAMS . . VAR (0x7fe5318086d8) (mode = VAR, assigned = false) "obj" . DECLS . . VARIABLE (0x7fe5318086d8) (mode = VAR, assigned = false) "obj" . . VARIABLE (0x7fe531808780) (mode = CONST, assigned = false) "bar" . BLOCK NOCOMPLETIONS at -1 . . EXPRESSION STATEMENT at 50 . . . INIT at 50 . . . . VAR PROXY local[0] (0x7fe531808780) (mode = CONST, assigned = false) "bar" . . . . ADD at 58 . . . . . PROPERTY at 54 . . . . . . VAR PROXY parameter[0] (0x7fe5318086d8) (mode = VAR, assigned = false) "obj" . . . . . . NAME foo . . . . . LITERAL 1 . RETURN at 67 . . ADD at 78 . . . VAR PROXY local[0] (0x7fe531808780) (mode = CONST, assigned = false) "bar" . . . LITERAL "1"

Above is V8 Output AST Syntax tree format , Although the presentation is not very intuitive , But it's essentially the same as babel / acorn etc. JavaScript Parser Compiled AST Tree It's the same , They all follow ESTree standard . Convert it to our familiar format as follows :
{ "type": "Program", "body": [ { "type": "FunctionDeclaration", "id": { "type": "Identifier", "name": "foo" }, "params": [ { "type": "Identifier", "name": "obj" } ], "body": { "type": "BlockStatement", "body": [ { "type": "VariableDeclaration", "declarations": [ { "type": "VariableDeclarator", "id": { "type": "Identifier", "name": "bar" }, "init": { "type": "BinaryExpression", "left": { "type": "MemberExpression", "object": { "type": "Identifier", "name": "obj" }, "property": { "type": "Identifier", "name": "foo" }, }, "operator": "+", "right": { "type": "Literal", "value": 1, "raw": "1" } } } ], }, { "type": "ReturnStatement", "start": 51, "end": 67, "argument": { "type": "BinaryExpression", "left": { "type": "Identifier", "name": "bar" }, "operator": "+", "right": { "type": "Literal", "value": "1", "raw": "'1'" } } } ] } } ], }

For the transcoding AST after , It's done right JavaScript A structured representation of the code , The compiler can operate the source code accordingly , It's generating AST At the same time , It also generates a corresponding scope , For example, the above code will produce the following scope content :
Global scope: global { // (0x7f91fb010a48) (0, 51) // will be compiled // 1 stack slots // temporary vars: TEMPORARY .result; // (0x7f91fb010ef8) local[0] // local vars: VAR foo; // (0x7f91fb010e68)
function foo () { // (0x7f91fb010ca8) (20, 51) // lazily parsed // 2 heap slots } } Global scope: function foo () { // (0x7f91fb010c60) (20, 51) // will be compiled }

The above line generates a global scope , We can see foo Variables are added to the global scope .

1、 Bytecode

After completing the above steps , Interpreter Ignition Will be based on AST Generate the corresponding bytecode
because JavaScript Bytecode is not currently associated with JVM or ESTree So standardized , So the format will be the same as V8 Engine versions are closely related .

2、 Understand a bytecode

Bytecode is an abstraction of machine code , If bytecode is physical CPU Design with the same calculation model , It's easier to compile bytecode into machine code , This means that interpreters are often registers or stacks . In other words Ignition Is a register with an accumulator .

V8 Byte dock file bytecodes.h Defines all kinds of bytecodes . The description blocks of these bytecodes can be combined to form any JavaScript function .

A lot of bytecodes are regular /^(Lda|Sta).+$/ Some of them a Anaphora accumulator (accumulator), Mainly used to describe the operation of the value into the accumulator register , Or take out the current value in the accumulator and store it in the register . So the interpreter can be understood as a register with an accumulator

The above case code is passed through V8 The interpreter outputs JavaScript The bytecode is as follows :
[generated bytecode for function: foo (0x3a50082d25cd <SharedFunctionInfo foo>)] Bytecode length: 14 Parameter count 2 Register count 1 Frame size 8 OSR nesting level: 0 Bytecode Age: 0 0x3a50082d278e @ 0 : 28 03 00 01 LdaNamedProperty a0, [0], [1] 0x3a50082d2792 @ 4 : 41 01 00 AddSmi [1], [0] 0x3a50082d2795 @ 7 : c6 Star0 0x3a50082d2796 @ 8 : 12 01 LdaConstant [1] 0x3a50082d2798 @ 10 : 35 fa 03 Add r0, [3] 0x3a50082d279b @ 13 : ab Return Constant pool (size = 2) 0x3a50082d275d: [FixedArray] in OldSpace - map: 0x3a5008042205 <Map> - length: 2 0: 0x3a50082d2535 <String[3]: #foo> 1: 0x3a500804494d <String[1]: #1> Handler Table (size = 0) Source Position Table (size = 0)

Let's take a look first foo Bytecode output of function ,LdaNamedProperty a0, [0], [1] take a0 The named properties are loaded into the accumulator ,a[i] Medium i It means arguments[i-1] That is, the second part of the function i Parameters .

So this operation is to take the first parameter of the function and put it into the accumulator , trailing [0] Express Yes. 0: 0x30c4082d2535 <String[3]: #foo> , That is to say

final [1] Represents the feedback vector index , The feedback vector contains... For performance optimization runtime Information . In short, it's to put Put it in the accumulator .

Then AddSmi [1], [0] Let the value in the accumulator sum to [1] Add up , Because it's a number 1 So it doesn't exist in the corresponding table . Finally, the value in the accumulator has been stored as 2.

final [0] Because we define a variable to store the result of the accumulator , So the bytecode also corresponds to the storage code of the response Star0 Indicates that the value of the corresponding accumulator is taken out and stored in the register r0 in .

LdaConstant [1] It means to take the second in the corresponding table [i] The elements are stored in the accumulator , That is to take out 1: 0x3a500804494d <String[1]: #1>, Into the accumulator .

Add r0, [3] Represents the value of the current accumulator '1' And registers r0 Value :2 Add up , final [3] Represents the feedback vector index

final Return Returns the value of the current accumulator '21'. The return statement is a function Foo() Introduction to , here Foo The caller of the function can get the corresponding value through the accumulator , And further processing .

3、 The use of bytecode

Because bytecode is an abstraction of machine code , So at run time, our code will be handed over to V8 To be more friendly , Because if V8 Enter the bytecode directly , You can skip the corresponding use Parser Generate corresponding AST Tree process , In other words, there will be a big improvement in performance , And there is also a very good security .

Because bytecode goes through a complete compilation process , The extra semantic information carried in the source code is erased , Its reverse difficulty can be compared with that of traditional compiled languages .

stay npm We found Bytenode, It is Act on Node.js Bytecode compiler for ( bytecode compiler ), Can put the JavaScript Compile into the real V8 Bytecode to protect the source code , At present, the author also saw that someone had a detailed sharing of this application , Details can be found in the references at the end of the paper - Include... In bytecode node.js The principle of source code .

4、 Interpretation and compilation execution of just in time compilation

After bytecode generation ,V8 There are two links to the compilation process , Regular code will execute bytecode directly , Executed directly by the bytecode compiler . Processing bytecode parser   I don't know about it , Let's take it as bytecode first, and then gcc Processing into machine code execution .

When we find that there is repeated code in the execution code ,V8 The monitor for will mark it as hot code , And submit it to the compiler TurboFan perform ,TurboFan Will compile bytecode into Optimized Machine Code, The execution efficiency of optimized machine code will be greatly improved .

however JavaScript It's a dynamic language , There's a lot of runtime state information , So our data structure can be modified at runtime , The machine code optimized by compiler can only deal with fixed structure , So once the machine code optimized by the compiler is dynamically modified , Then the machine code will be invalid , The compiler needs to execute Anti optimization operation , hold Optimized Machine Code Recompile back to bytecode .

JavaScript Object

JavaScript It's a door Based on the object (Object-Based) Language , so to speak JavaScript In addition to null,undefined   Most of the content is made up of objects except for special representations such as , We can even say JavaScript It's a language built on objects .

however JavaScript It's not strictly an object-oriented language , This is also because object-oriented languages need to support encapsulation by nature 、 Inherit 、 polymorphic . however JavaScript There is no direct polymorphism support , But we can still achieve polymorphism , It's just that it's more difficult to realize .

JavaScript The object structure of is very simple , It consists of a set of components and values , There are three types of values :
  • The original type : The original types mainly include :null、undefined、boolean、number、string、bigint、symbol, Store in a stack like data structure , Follow the principle of first in, then out , And it has immutable characteristic , For example, we modified string Value ,V8 Will return to us with a brand new string.

  • object type :JavaScript It's a language built on objects , So the attribute value of an object can also be another object .

  • Function type : If a function is a property of an object , We generally call it a method .


Function as JavaScript First class citizens in , It can be very flexible to achieve a variety of functions . The fundamental reason is that JavaScript The function in is a special kind of object .

Because functions are designed by first-class citizens , our JavaScript Can be very flexible to achieve closure and functional programming and other functions . Functions can be called by adding parentheses to the function name :

function foo(obj) { const bar = + 1 return bar + '1' }
foo({foo: 1});

You can also use anonymous functions ,IIFE Way to call , actually IIFE Method only supports receive expressions , But the function in the following example is a statement , therefore V8 Will implicitly put function statements foo Understand it as a functional expression foo, To run .
stay ES6 Before module scope appears ,JavaScript There is no concept of private scope in , So when multiple people develop projects , Singleton mode is often used , With IIFE Create a namespace To reduce the problem of global variable naming conflict . therefore IIFE The biggest feature is that the implementation will not pollute the environment , Functions and variables inside functions are not accessed by other parts of the code , The outside can only get IIFE Return result of .
(function foo(obj) { const bar = + 1
return bar + '1' })({foo: 1})

Since functions are essentially objects , So how does a function get callable features that are different from other objects ?
V8 Internally, in order to handle the callability of functions , Hidden properties are added to each function , As shown in the figure below :

The hidden properties are functions name Properties and code attribute .
  • name Properties are widely supported by browsers , But until ES6 To write it into the standard ,ES6 Previous name Property can get the function name , Because V8 The corresponding interfaces are exposed .Function Function instance returned by constructor ,name The value of the property is anonymous

(new Function).name // "anonymous"

  • code Property represents the function code , With string Stored in memory in the form of . When a function call statement is executed ,V8 Will be taken from the function object code Property value , Then explain and execute the function code .V8 No exposure code attribute , So you can't output it directly .

2、About JavaScript

JavaScript Can pass new Keyword to generate the corresponding object , But there are many details hidden in it, which makes it easy to increase the cost of understanding .

In fact, it's out of Market Research , because JavaScript The birth period of ,Java Very popular , and JavaScript Need to look like Java , But not with Java Conduct battle.

therefore JavaScript Not just the name , At the same time, I joined in new. So the construction object becomes what we see . This is unreasonable in design , But it does help promote JavaScript degree of heat .

in addition ES6 Added class characteristic , but class At the root, it's still based on the inheritance of the prototype chain , In the history of development, people try to ES4 Before and after in order to achieve the real class efforts , But they all failed , So I finally decided not to do what's really right , So what we're using now is class In the true sense JS VM Grammatical sugar , But it's not the same as what we use in projects babel It's essentially different to convert to a function and then execute it ,V8 When compiling a class, corresponding keywords will be given for processing .

3、Object Storage

JavaScript It's object-based , So the value types of objects are also very rich . It gives us flexibility at the same time , Object storage data structure with linear data structure has been unable to meet the requirements , You have to use nonlinear data structures ( Dictionaries ) For storage . This brings the problem of inefficient object access . therefore V8 In order to improve the efficiency of storage and search , Adopted a complex storage strategy .

First we create objects foo, And print it , The relevant code is as follows :
function Foo() { this["bar3"] = 'bar-3' this[10] = 'foo-10' this[1] = 'foo-1' this["bar1"] = 'bar-1' this[10000] = 'foo-10000' this[3] = 'foo-3' this[0] = 'foo-0' this["bar2"] = 'bar-2' }
const foo = new Foo()
for(key in bar){ console.log(`key: ${key} value:${foo[item]}`) }

The result of the code output is as follows :
key: 0 value:foo-0 key: 1 value:foo-1 key: 3 value:foo-3 key: 10 value:foo-10 key: 10000 value:foo-10000 key: bar3 value:bar-3 key: bar1 value:bar-1 key: bar2 value:bar-2

After careful observation , You can find V8 Implicitly handles the ordering of objects .
  • key Attributes for numbers are printed first , And in ascending order .

  • key The attributes of a string are arranged in the order in which they were defined .

The reason for this is that ECMAScript The specification defines that numeric attributes should be arranged in ascending order of index values , String properties are sorted in ascending order according to the order in which they were created .V8 As ECMAScript Of course, the implementation of the standard needs to be strictly observed .

To optimize the access efficiency of objects ,V8 adopt key Divide objects into two categories .
  • In object key The property of a number is called elements( Sort properties ), Such attributes trade time for space , Direct subscript access , Improve access speed . When element When the serial number of is very discontinuous , Will be optimized into hash surface .

  • In object key The property for a string is called properties( General properties ), By dividing the attributes and values of an object into linear data structure and attribute dictionary structure , To optimize the original full dictionary storage .properties By default, the attribute adopts the linked list structure , When the amount of data is small , The search will be quick , But when the amount of data goes up to a certain value , Will be optimized into hash surface . The above objects are stored in memory, as shown in the figure :

After the storage decomposition , The access of the object will search the corresponding attribute according to the category of the index value , If it's a full index of attribute values , that V8 From elements Read elements in ascending order in , Go again properties Read the remaining elements in .

It is worth noting that V8 Yes ECMAScript The implementation of is inert , In memory V8 It did not element The elements are arranged in ascending order .

4、 Properties within objects

V8 After dividing objects into two categories by attributes , Simplify the efficiency of object search , But there will be one more step , For example, I need to visit Foo.bar3,v8 You need to access the corresponding object first Foo, Visit the corresponding properties In order to get bar3 Corresponding value , To simplify the operation , V8 Will be the object of properties Property default assignment 10 Attributes within objects (in-object properties) As shown in the figure below :

When properties Insufficient attributes 10 Time , be-all properties All attributes can be in object attributes , When more than 10 Time , exceed 10 Of properties attribute , Refill to properties The dictionary structure is used for storage . After using in object properties , It's much more convenient to find the corresponding attribute again .
The attributes in an object can be expanded dynamically .The number of in-object properties is predetermined by the initial size of the object. However, the author has not seen that the attribute in the object is larger than 10 Circumstances .

So that's the analysis , Students can think about the daily development of which operations will be very detrimental to the efficiency of the above rules , such as delete In general, it is not recommended to use , It operates on the value of an object property , Because deleting elements will cause a large number of attribute elements to move , and properties You may also need to rearrange the attributes inside the object , It's the cost of extra performance ; Without affecting the semantic fluency of the code , have access to undefined Reset the property value , Or use Map data structure ,Map.delete The optimization of is better .

In object properties don't apply to all scenes , In the case of too many object attributes or frequent changes of object attributes , V8 Will cancel the assignment of attributes within the object , All degraded to non-linear dictionary storage mode , Although this reduces the search speed , But it improves the speed of modifying the properties of an object . for example :
function Foo(_elementsNum, _propertiesNum) { // set elements for (let i = 0; i < _elementsNum; i++) { this[i] = `element${i}`; } // set property for (let i = 0; i < _propertiesNum; i++) { let ppt = `property${i}`; this[ppt] = ppt + 'value'; } } const foos = new Foo(100, 100);

Instantiation foos After the object , We look at the corresponding memory properties, You can find all the property${i} Attributes are all in properties in , Because too much has been V8 It has been degraded .

1) Compiler optimization

Take the code above as an example , Let's create a larger object instance
const foos = new Foo(10000, 10000

Because the constructors that we create objects are fixed structures , So in theory, it triggers the monitor to mark hot code , Give it to the compiler for optimization , Let's see V8 Output record of :
[marking 0x2ca4082d26e5 <JSFunction Foo (sfi = 0x2ca4082d25f5)> for optimized recompilation, reason: small function] [compiling method 0x2ca4082d26e5 <JSFunction Foo (sfi = 0x2ca4082d25f5)> (target TURBOFAN) using TurboFan OSR] [optimizing 0x2ca4082d26e5 <JSFunction Foo (sfi = 0x2ca4082d25f5)> (target TURBOFAN) - took 1.135, 3.040, 0.287 ms] [marking 0x2ca4082d26e5 <JSFunction Foo (sfi = 0x2ca4082d25f5)> for optimized recompilation, reason: small function] [compiling method 0x2ca4082d26e5 <JSFunction Foo (sfi = 0x2ca4082d25f5)> (target TURBOFAN) using TurboFan OSR] [optimizing 0x2ca4082d26e5 <JSFunction Foo (sfi = 0x2ca4082d25f5)> (target TURBOFAN) - took 0.596, 1.681, 0.050 ms]

You can see that the corresponding optimization record is output , But the author did not make a more in-depth study on it , If you know more about compiler optimization , You can add... In the comment area .

2) About proto

JavaScript It's a very distinctive inheritance , It's inheritance using a prototype chain , use _proto_ As a bridge of links . however V8 It is not recommended to use it directly _proto_ Direct manipulation of object inheritance , Because it involves V8 Hide class related , Will destroy V8 When the object instance is generated, the hidden Class Optimization and the corresponding class offset have been done (class transition) operation .

JavaScript Type system

JavaScript The type system in is a very basic knowledge point , But it's also the most widely used and flexible , The situation is complex and error prone , The main reason is that the conversion rules of type system are cumbersome , And it's easy for engineers to ignore its importance .

stay CPU The processing of data in is just shift , Add or multiply , There is no concept of related types , Because it's dealing with a bunch of binary code . But in high-level languages , Language compilers need to determine whether the addition of different types of values has a corresponding meaning .

Like JavaScript It's also a weakly typed language python Enter the following code 1+'1'
In[2]: 1+'1'
Traceback (most recent call last): File "..", line 1, in run_code exec(code_obj, self.user_global_ns, self.user_ns) File "<ipython-input-2-0cdad81f9201>", line 1, in <module> 1+'1' TypeError: unsupported operand type(s) for +: 'int' and 'str'

You can see the corresponding TypeError Error of , But this code is JavaScript You can't make a mistake , Because it's considered meaningful code .
console.log(1+'1') // 11

The internal cause of the above phenomenon is the type system . The more powerful the type system is , The more content the compiler can detect . It can affect more than the definition of a type , And then there's the type checking , And the definition of different types of previous operational interactions .
In Wikipedia , type The system is defined like this : In computer science , Type system (type system) Used to define how to classify numbers and expressions in a programming language into many different types , How to operate these types , How these types interact . Type can identify a value or a group of values with a specific meaning and purpose ( Although some types , Such as abstract type and function type , While the program is running , May not be represented as a value ). The type system is very different between languages , Maybe , The main difference lies in the syntax at compile time , And the operation mode of the runtime .

1、 Type system basic conversion

ECMAScript Defined JavaScript The specific operation rules in .
1.Let lref be the result of evaluating AdditiveExpression. 2.Let lval be GetValue(lref). 3.ReturnIfAbrupt(lval). 4.Let rref be the result of evaluating MultiplicativeExpression. 5.Let rval be GetValue(rref). 6.ReturnIfAbrupt(rval). 7.Let lprim be ToPrimitive(lval). 8.ReturnIfAbrupt(lprim). 9.Let rprim be ToPrimitive(rval). 10.ReturnIfAbrupt(rprim). 11.If Type(lprim) is String or Type(rprim) is String, then a.Let lstr be ToString(lprim). b.ReturnIfAbrupt(lstr). c.Let rstr be ToString(rprim). d.ReturnIfAbrupt(rstr). e.Return the String that is the result of concatenating lstr and rstr. 12.Let lnum be ToNumber(lprim). 13.ReturnIfAbrupt(lnum). 14.Let rnum be ToNumber(rprim). 15.ReturnIfAbrupt(rnum). 16.Return the result of applying the addition operation to lnum and rnum. See the Note below

The rules are complicated , Let's break it down and introduce it . Take addition for example , Let's start with the standard type , If you add a number to a string , Where as long as the string appears ,V8 Other values will be processed into strings , for example :
const foo = 1 + '1' + null + undefined + 1n
// Expression is V8 Convert to const foo = Number(1).toString() + '1' + String(null) + String(undefined) + BigInt(1n).toString()
// "11nullundefined1"

If the content involved in the operation is not the underlying type , according to ECMAScript From a normative point of view ,V8 Implemented a ToPrimitive Method , Its function is to convert compound types into corresponding basic types .ToPrimitive According to the object to string conversion or object to number conversion , There are two sets of rules :
type NumberOrString = number | string
type PrototypeFunction<T> = (input: Record<string, any>, flag:T) => T
type ToPrimitive = PrototypeFunction<NumberOrString>

From the above TypeScript The type can tell , Even though objects use ToPrimitive convert , But according to the transfer parameter of the second parameter , The final treatment will be different . The corresponding values of different parameters are given below ToPrimitive Processing flow chart :

Corresponding ToPrimitive(object, Number), The processing steps are as follows :

  • If object For the basic type , Direct return

  • otherwise , call valueOf Method , If you return a raw value , be JavaScript Returns it .

  • otherwise , call toString Method , If you return a raw value , be JavaScript Returns it .

  • otherwise ,JavaScript Throw a TypeError abnormal .

Corresponding ToPrimitive(object, String), The processing steps are as follows :

  • If object For the basic type , Direct return

  • otherwise , call toString Method , If you return a raw value , be JavaScript Returns it .

  • otherwise , call valueOf Method , If you return a raw value , be JavaScript Returns it .

  • otherwise ,JavaScript Throw a TypeError abnormal .

among ToPrimitive The second parameter of is not required , The default value is number however date Type is the exception , The default value is string.

Let's take a look at a few examples , Check it out :
/* Patients with a */ { foo: 'foo' } + { bar: 'bar' } // "[object Object][object Object]"
/* Example 2 */ { foo: 'foo', valueOf() { return 'foo'; }, toString() { return 'bar'; }, } + { bar: 'bar', toString() { return 'bar'; }, } // "foobar"
/* Example 3 */ { foo: 'foo', valueOf() { return Object.create(null); }, toString() { return Object.create(null); }, } + { bar: 'bar', } // Uncaught TypeError: Cannot convert object to primitive value
/* Example 4 */ const date = new Date(); date.valueof = () => '123'; date.toString = () => '456'; date + 1; // "4561"
Example 3 will report an error , because ToPrimitive Cannot convert to underlying type .


utilize V8 In depth understanding of JavaScript, This title may be a bit crazy , But for the author, through this study really further understanding JavaScript Even the working mechanism of other languages , At the same time, I think deeply about the concepts of front-end and technology stack .

This article mainly through the daily simple code storage leads to V8 Related and computer science concepts , from JavaScript The reason for the current design , And the combination of V8 Workflow gives a macro view of ; Then through the detailed steps to show the complete V8 It's the product of every part of the pipeline ; Through analysis JavaScript Object leads to its storage rules ; Finally, the type system leads to V8 Implementation of rules for interacting with different types of data .

about V8 For the huge and complex execution structure, this paper only describes a few , There are too many topics in the article that can be used to extend and lead to more worthy of study , I hope that students can gain and think through this article , If there are any mistakes in the article, please point out in the comments section .

Reference material

  • Concurrent marking in V8
  • How JavaScript works: inside the V8 engine + 5 tips on how to write optimized code
  • Wikipedia - Type system
  • ECMAScript 2015 Language Specification
  • ECMAScript
  • Understanding V8’s Bytecode
  • Include... In bytecode node.js The principle of source code

Go to
The color

【 Dry cargo sharing 】 babel-plugin-import The most complete source code

Development and application of Google plug in

Talk about technical dry goods IOC I'm going to do those things (Dependency inject)

More ways of technical communication

Want to have a face-to-face technical exchange ? Want to get involved in the live broadcast in time ? Can scan the code to join the nail group “ Kangaroo cloud open source framework technology exchange group ”( Group number :30537511)

Want to experience more stack open source projects ? Can be in Github Community or Gitee Community search “FlinkX” Open source project

Github Open source project address :

Gitee Open source project address :

Click on Read the original , One click arrival FlinkX Open source project !

本文为[Shuzhan Study Club]所创,转载请带上原文链接,感谢

  1. HTML + CSS + JavaScript to achieve cool Fireworks (cloud like particle text 3D opening)
  2. HTML + CSS + JavaScript realizes 520 advertising love tree (including music), which is necessary for programmers to express themselves
  3. Solve the problem of Web front-end deployment server (it can be deployed online without a server)
  4. HTML + CSS + JS make wedding countdown web page template (520 / Tanabata Valentine's Day / programmer advertisement)
  5. What else can driverless minibus do besides "Park connection"?
  6. Cloud native leads the era of all cloud development
  7. NRM mirror source management tool
  8. Bring it to you, flex Jiugong
  9. Lolstyle UI component development practice (II) -- button group component
  10. Deconstruction assignment in ES6
  11. Luo 2 peerless Tang clan was officially launched. The official gave a key point, and the broadcast time was implied
  12. 20初识前端HTML(1)
  13. 当新零售遇上 Serverless
  14. 20 initial knowledge of front-end HTML (1)
  15. When new retail meets serverless
  16. [golang] - go into go language lesson 5 type conversion
  17. [golang] - go into go language lesson 6 conditional expression
  18. HTML5(八)——SVG 之 path 详解
  19. HTML5 (8) -- detailed explanation of SVG path
  20. 需要开通VIP以后页面内容才能复制怎么办?控制台禁用javascript即可
  21. Web前端|CSS入门教程(超详细的CSS使用讲解,适合前端初学者)
  22. 实践积累 —— 用Vue3简单写一个单行横向滚动组件
  23. Serverless 全能选手,再下一城
  24. What if you need to open a VIP to copy the page content? Just disable JavaScript on the console
  25. Web front end | CSS introductory tutorial (super detailed CSS explanation, suitable for front-end beginners)
  26. Practice accumulation - write a single line horizontal scroll component simply with vue3
  27. Dili Reba is thin again. She looks elegant and high in a strapless hollow skirt, and her "palm waist" is beautiful to a new height
  28. Serverless all-round player, next city
  29. The difference between MySQL semi synchronous replication and lossless semi synchronous replication
  30. Vue表单设计器的终极解决方案
  31. The ultimate solution for Vue form designer
  32. Nginx从理论到实践超详细笔记
  33. Yu Shuxin's red backless swimsuit is split to the waist and tail, with a concave convex figure and excessive color matching, and his face is white to dazzling
  34. Nginx ultra detailed notes from theory to practice
  35. 【动画消消乐|CSS】086.炫酷水波浪Loading过渡动画
  36. typecho全站启用https
  37. CCTV has another popular employee. The off-site interpretation is very professional, and the appearance ability is no less than that of Wang Bingbing
  38. [animation Xiaole | CSS] 086. Cool water wave loading transition animation
  39. Enable HTTPS in Typecho
  40. 50天用JavaScript完成50个web项目,我学到了什么?
  41. 根据JavaScript中原生的XMLHttpRequest实现jQuery的Ajax
  42. What have I learned from completing 50 web projects with JavaScript in 50 days?
  43. "My neighbor doesn't grow up" has hit the whole network. There are countless horse music circles, and actor Zhou Xiaochuan has successfully made a circle
  44. 根据JavaScript中原生的XMLHttpRequest实现jQuery的Ajax
  45. Implement the Ajax of jQuery according to the native XMLHttpRequest in JavaScript
  46. Implement the Ajax of jQuery according to the native XMLHttpRequest in JavaScript
  47. 30 + women still wear less T-shirts and jeans. If they wear them like stars, they will lose weight
  48. 数栈技术分享前端篇:TS,看你哪里逃~
  49. Several stack technology sharing front end: TS, see where you escape~
  50. 舍弃Kong和Nginx,Apache APISIX 在趣链科技 BaaS 平台的落地实践
  51. Abandon the landing practice of Kong and nginx, Apache apisik on the baas platform of fun chain technology
  52. 浪迹天涯king教你用elementui做复杂的表格,去处理报表数据(合并表头,合并表体行和列)
  53. 前端HTML两万字图文大总结,快来看看你会多少!【️熬夜整理&建议收藏️】
  54. Wandering around the world king teaches you to use elementui to make complex tables and process report data (merge header, merge table body rows and columns)
  55. 路由刷新数据丢失 - vuex数据读取的问题
  56. Front end HTML 20000 word graphic summary, come and see how much you can【 Stay up late to sort out & suggestions]
  57. Route refresh data loss - vuex data reading problem
  58. Systemctl系统启动Nginx服务脚本
  59. Systemctl system startup nginx service script
  60. sleepless