DEV Community

Priyadharshini Selvaraj
Priyadharshini Selvaraj

Posted on

Mastering JavaScript Internals#2 The Parser & AST

The Parser: How JavaScript Actually Reads Your Code

You type some JavaScript. You hit run. And it works.

But between those two moments, something fascinating happens β€” your code gets read, understood, and transformed before a single instruction executes. That process is called parsing, and it's the very first thing the engine does.

Let's walk through it, step by step.


πŸ—ΊοΈ The Big Picture: 3 Steps Before Execution

When the JavaScript engine receives your code, it does three things in order:

Your Code (text)
      ↓
1. Tokenizing  β†’  breaks code into small labelled pieces
      ↓
2. Parsing     β†’  builds a tree structure from those pieces
      ↓
3. Compiling   β†’  turns the tree into something it can run
Enter fullscreen mode Exit fullscreen mode

Today we're focusing on steps 1 and 2. Step 3 (compilation & JIT) is Post 3.


Step 1: Tokenizing β€” Breaking Code into Pieces

The very first thing the engine does is read your source code character by character and group them into meaningful chunks called tokens.

Here's a simple example:

const age = 25;
Enter fullscreen mode Exit fullscreen mode

The engine sees this as:

Token Type
const Keyword
age Identifier
= Operator
25 Number literal
; Punctuation

That's it. The tokenizer doesn't care about meaning yet β€” it just labels every piece of your code.

πŸ’‘ Confused? Think of tokenizing like a spell-checker reading a sentence. It doesn't understand the sentence yet β€” it just identifies each word and punctuation mark one at a time. Only after that does it start to make sense of the grammar.


Step 2: Parsing β€” Building the Tree (AST)

Once the engine has a list of tokens, it needs to understand the structure of your code β€” how those tokens relate to each other.

It does this by building an Abstract Syntax Tree, or AST.

πŸ’‘ Confused by "Abstract Syntax Tree"? Don't let the name intimidate you. It's just a tree diagram that represents your code's structure. "Abstract" means it strips away things like whitespace and semicolons β€” only the meaningful structure remains. "Syntax" means it's based on the grammar rules of JavaScript. "Tree" because it branches out like a family tree.

Here's what the AST looks like for our example:

const age = 25;
Enter fullscreen mode Exit fullscreen mode
VariableDeclaration (const)
└── VariableDeclarator
    β”œβ”€β”€ Identifier: age
    └── NumericLiteral: 25
Enter fullscreen mode Exit fullscreen mode

Even for one line of code, there's a whole tree. Let's try something slightly more complex:

function greet(name) {
  return "Hello, " + name;
}
Enter fullscreen mode Exit fullscreen mode
FunctionDeclaration
β”œβ”€β”€ Identifier: greet
β”œβ”€β”€ Param: Identifier (name)
└── BlockStatement
    └── ReturnStatement
        └── BinaryExpression (+)
            β”œβ”€β”€ StringLiteral: "Hello, "
            └── Identifier: name
Enter fullscreen mode Exit fullscreen mode

Notice how the tree captures the nesting of your code β€” the return is inside the function, the + operation is inside the return, and so on.

πŸ’‘ Why does this matter to me as a developer? Every modern tool you use β€” Babel, ESLint, Prettier, TypeScript β€” reads and manipulates this tree. When ESLint catches a bug or Prettier reformats your code, it's working with the AST. Understanding this helps you understand your tools.


πŸš€ Lazy vs. Eager Parsing: A Smart Shortcut

Here's something interesting: the engine doesn't fully parse all your code upfront.

It uses two modes:

  • Eager parsing β€” fully parse this right now, we'll need it immediately.
  • Lazy parsing β€” just skim this for now, fully parse it later if it's actually called.
// This gets eagerly parsed β€” it runs immediately
console.log("app started");

// This gets lazily parsed β€” it's a function, only parsed fully when called
function heavySetup() {
  // ... lots of code
}
Enter fullscreen mode Exit fullscreen mode

πŸ’‘ Confused? Lazy parsing is like skimming a book's table of contents before reading each chapter in full. The engine skims functions it hasn't needed yet, saving time on startup. This is why large JavaScript apps can start up faster than you'd expect β€” the engine defers work it doesn't need right away.

This is also why wrapping everything in functions can sometimes hurt startup time β€” if the engine has to parse more eagerly than expected.


❌ When Parsing Goes Wrong: Syntax Errors

If your code breaks a grammar rule, the parser throws a SyntaxError before anything runs.

const x = ; // SyntaxError: Unexpected token ';'
Enter fullscreen mode Exit fullscreen mode

The parser expected a value after =, but got ; instead. It can't build a valid tree from this, so it stops immediately.

πŸ’‘ Confused? This is why syntax errors feel different from runtime errors. A runtime error happens during execution (e.g. TypeError: cannot read property of undefined). A syntax error happens before execution β€” the engine couldn't even read your code properly. Nothing runs until the syntax is valid.


πŸ› οΈ You Can See the AST Yourself

Want to explore this hands-on? Visit astexplorer.net and paste any JavaScript code. You'll see the full AST in real time β€” every node, every branch.

Try pasting:

const double = (n) => n * 2;
Enter fullscreen mode Exit fullscreen mode

You'll immediately see how even a simple arrow function becomes a rich tree structure. It's the best way to make this click.


πŸ“ Quick Recap

Here's what happens before your code runs:

  1. Tokenizing β€” the engine reads your source text and splits it into labelled tokens.
  2. Parsing β€” tokens are assembled into an AST that captures the structure of your code.
  3. The engine uses lazy parsing to skip functions it doesn't need yet β€” saving startup time.
  4. If your code has a grammar mistake, a SyntaxError is thrown and nothing runs.

➑️ Up Next

Post 3 β€” JIT Compilation: How JavaScript Goes From Slow to Fast

Now that the engine has an AST, it needs to turn it into something the computer can actually execute. We'll cover bytecode, the Ignition interpreter, and how Turbofan turns your hot functions into blazing-fast machine code.


Tried astexplorer.net? Drop a comment with something surprising you found in your own code's AST!


πŸ”— Connect with Me

If you found this post helpful, follow me on Dev.to for more insights on JavaScript internals, data structures, and algorithms!

Top comments (0)