πŸ“’
SDV503
  • SDV503
  • Command Prompt
    • Windows Command Prompt
    • Mac OS Terminal
  • GIT & GITHub
    • GitHub
    • Git
      • Git Workflow
        • Forking Workflow
  • README
    • How to write a readme.md file?
    • Write a better README.me
    • Generate a README for GitHub Repo Quickly
  • Code-comments
    • Clean Comments
    • Writing Comments in a Good way
  • Pair Coding
    • What is Pair Coding?
    • Pair Programming Experience
  • Programming?
    • What is Programming?
    • What Is A Programming Paradigm?
    • JavaScript Programming Paradigms
  • Number Systems
    • Decimal and Binary numbers
  • JavaSCript
    • Introduction To JavaScript
      • The JavaScript Engine
  • JS Call Stack
    • JavaScript call stack
    • JavaScript & Memory
      • Memory Leaks in JavaScript
    • Execution Context and Execution Stack in Javascript
      • Javascript Execution Context and Hoisting
  • JavaScript Variables
    • Introduction to JS Data Types
    • Primitive and Non-Primitive
    • Operator precedence and associativity
      • JS Operators Part One
      • JS Operators Part Two
      • JS Operators Part Three
    • Clean Code for JS Variables
  • JavaScript Scopes
    • Scope (Chain) Visualized
  • Javascript β€Šβ€”β€Šthis keyword
  • JavaScript Data Types
    • More JavaScript Data Types
  • JavaScript Expressions and Statements
  • if/else, switch and ternary operator
    • If/Else statement
  • Switch Statement
  • Ternary Operator
  • JavaScript Loops
    • Loops in JavaScript
      • Trouble With Loops
  • Objects
    • JavaScript Objects
      • Prototypal Inheritance Visualized
      • JavaScript Number Object
      • JavaScript String Object
  • Functions
    • JavaScript Function Part One
    • JavaScript Function Part Two
    • Immediately Invoked Function Expressions ~ IIFE
    • JS => Arrow Functions
    • JavaScript Callback
    • Hoisting in JavaScript
      • Hoisting Visualized
    • Recursion Functions
    • Curry and Function Composition
  • JavaScript Features
    • JSpread Operator
    • JavaScript Built-in Functions & Objects
  • Data Structures&Algorithms
    • JavaScript’s Data Types
    • Data Structures in JavaScript
      • Introduction to Big O Notation
      • Big O Notation in Javascript
      • Linked Lists
        • Linked Lists β€” 2
      • Hash Tables
      • Stack & Queue
  • TLDR
    • Single quotes (β€˜ ’) and double quotes (β€œ ”) in JavaScript
  • ES6
    • Generators and Iterators
    • Javascript Classes
    • JavaScript closures
    • JavaScript Promises & Async/Await
      • Event Loop Visualized
  • C#
    • What does C#? (C Sharp)
    • C# vs JavaScript
    • What Is The Difference Between C#, .NET, ASP.NET, Microsoft.NET, and Visual Studio?
    • What is the .NET Framework?
    • Methods and Properties of Console Class in C#
    • Datatypes in C#
    • C# Code Comments
    • The if statement
    • The switch statement
    • Loops
    • Comparison operators
    • Addition assignment operators
    • The String Interpolation Operator
    • Arrays
    • Lists
    • Dictionaries
Powered by GitBook
On this page

Was this helpful?

  1. JavaSCript
  2. Introduction To JavaScript

The JavaScript Engine

PreviousIntroduction To JavaScriptNextJavaScript call stack

Last updated 4 years ago

Was this helpful?

Cover image for πŸš€βš™οΈ JavaScript Visualized: the JavaScript Engine

JavaScript is cool, but how can a machine actually understand the code you've written? As JavaScript devs, we usually don't have to deal with compilers ourselves. However, it's definitely good to know the basics of the JavaScript engine and see how it handles our human-friendly JS code and turns it into something machines understand!

| Note: This topic is mainly based on the V8 engine used by Node.js and Chromium-based browsers.

The HTML parser encounters a script tag with a source. Code from this source gets loaded from either the , or an installed . The response is the requested script like a , which the byte stream decoder takes care of! The decodes the stream of bytes as it’s being downloaded.

The engine uses two parsers: the pre-parser, and the parser. In order to reduce the time it takes to load up a website, the engine tries to avoid parsing code that's not necessary right away. The preparser handles code that may be used later on, while the parser handles the code that’s needed immediately! If a certain function will only get invoked after a user clicks a button, it's not necessary that this code is compiled immediately just to load up a website. If the user eventually ends up clicking the button and requiring that piece of code, it gets sent to the parser.

Although byte code is fast, it can be faster. As this bytecode runs, information is being generated. It can detect whether certain behavior happens often, and the types of data that’s been used. Maybe you've been invoking a function dozens of times: it's time to optimize this so it'll run even faster! πŸƒπŸ½β€β™€οΈ

JavaScript is a dynamically typed language, meaning that the types of data can change constantly. It would be extremely slow if the JavaScript engine had to check each time which data type a certain value has.

In order to reduce the time it takes to interpret the code, optimized machine code only handles the cases the engine has seen before while running the bytecode. If we repeatedly used a certain piece of code that returned the same data type over and over, the optimized machine code can simply be re-used in order to speed things up. However, since JavaScript is dynamically typed, it can happen that the same piece of code suddenly returns a different type of data. If that happens, the machine code gets de-optimized, and the engine falls back to interpreting the generated byte code.

Say a certain function is invoked 100 times and has always returned the same value so far. It will assume that it will also return this value the 101st time you invoke it.

Let’s say that we have the following function sum, which’s (so far) always been called with numerical values as arguments each time:

This returns the number 3! The next time we invoke it, it will assume that we’re invoking it again with two numerical values.

If that’s true, no dynamic lookup is required, and it can just re-use the optimized machine code. Else, if the assumption was incorrect, it will revert back to the original byte code instead of the optimized machine code.

For example, the next time we invoke it, we pass a string instead of a number. Since JavaScript is dynamically typed, we can do this without any errors!

This means that the number 2 will get coerced into a string, and the function will return the string "12" instead. It goes back to executing the interpreted bytecode and updates the type of feedback.

I hope this post was useful to you! Of course, there are many parts to the engine that I haven't covered in this post (JS heap, call stack, etc.) which I might cover later! I definitely encourage you to start to do some research yourself if you're interested in the internals of JavaScript, V8 is open source and has some great documentation on how it works under the hood! πŸ€–

Alt Text

The byte stream decoder creates from the decoded stream of bytes. For example, 0066 decodes to f, 0075 to u, 006e to n, 0063 to c, 0074 to t, 0069 to i, 006f to o, and 006e to n followed by white space. Seems like you wrote function! This is a reserved keyword in JavaScript, a token gets created, and sent to the (and pre-parser, which I didn't cover in the gifs but will explain later). The same happens for the rest of the byte stream.

Alt Text

The parser creates nodes based on the tokens it receives from the byte stream decoder. With these nodes, it creates an or AST.

Alt Text

Next, it's time for the ! The interpreter walks through the AST and generates byte code based on the information that the AST contains. Once the byte code has been generated fully, the AST is deleted, clearing up memory space. Finally, we have something that a machine can work with!

Alt Text

The byte code, together with the generated type feedback, is sent to an . The optimizing compiler takes the byte code and type feedback, and generates highly optimized machine code from these.

Alt Text

|| ||

tokens
parser
Abstract Syntax Tree
interpreter
optimizing compiler
V8 Docs
V8 Github
Chrome University 2018: Life Of A Script
network
cache
service worker
stream of bytes
byte stream decoder