The Cracked Engineer’s Guide to JavaScript: Mechanics, Magic, and Misconceptions
After a career break from tech, I found myself going back to the roots of Software Engineering, exploring different areas such as AI and Rust. Along the way, I realised it would be valuable to document my findings and build a guide on how to be "cracked" as a Software Engineer—not just writing code, but truly understanding how things work under the hood. The reason is simple: with the rise of Generative AI, the barrier to entry for software engineers is lower than ever, but at the same time, the demands on existing engineers are only increasing. I believe we are shifting from an era of specialism to an era of breadth. Before tools like ChatGPT and Claude, companies hired experts in specific frameworks or tech stacks. Now, learning new technologies has become easier and faster, with automation handling many repetitive tasks. But this means that true engineering knowledge is more important than ever. Anyone can code. Few can engineer. To stand out, it’s crucial to understand the internals of the technologies we use—how they work, how they scale, and how they solve complex modern-day problems. With that in mind, let’s begin with JavaScript—the mechanics and magic behind it all. Understanding JavaScript’s Execution Model JavaScript is a single threaded programming language. This means it only handles one task at a time, unlike compiled languages such as Rust and C++, it can only execute one operation at a time. Here are some key features of JavaScript to note down It uses a call stack to keep track of function execution If we have a nested function - meaning a function that calls another function, it is added to the call stack, and popped off when the function has completed execution Blocking code (nested for loops or synchronous network calls) freezes everything as JavaScript can’t move forward until it is finished. So the question maybe, how does JavaScript handle tasks like API calls and timers if it is single threaded? Read through the whole page and you will find out! The Call Stack – "The Brain of JavaScript Execution" Before we jump in on how JavaScript handles things like API calls and timers, it is crucial to understand one core aspect of the language. The Call Stack, you can think of it as the “brain” of JavaScript as the subtitle of this section suggests. But before that, let’s briefly go over what a stack is in the realm of Computer Science. A stack is a data structure that can contain a list of elements, which has two operations. Push - adds an element into the Stack Pop - removes the recently added element from the Stack. When JavaScript executes a function, it adds it to the call stack, if that function calls another function inside, then that function is added on top of the call stack. Only until the execution is finished is when the function is popped off the stack. So we have a function named addition, when it is called at the bottom of the script, it is the first one added to the stack, inside the addition function, there’s a function called anotherFunction(), it will be the second one added to the stack, then inside that, there is someOtherFunction() being called, which is the last one added to the stack. Once someOtherFunction is finished executing, then it will be the first one to be popped off the stack, and the same goes for the other functions when they are finished with execution. The Event Loop & Async JavaScript – "How JavaScript Never Sleeps" At the beginning, I asked how JavaScript handles things like API calls if it is single-threaded. Enter the event loop. This is what allows concurrency, it allows non-blocking code by offloading it to the browser or the Node.js runtime environment. So there’s 3 components to bear in mind 1) The Call Stack - we went over this already in the previous section 2) Web API’s - JavaScript delegates asynchronous tasks (such as setTimeout, fetch, and DOM events) to the Web APIs provided by the browser (or Node.js). 3) Callback Queue & Microtask Queue - Once an async task completes, it can’t execute immediately. Instead, it goes into a task queue and waits for the event loop to process it. There are two types of queues: a) Callback Queue (Macrotask Queue) – Contains: setTimeout setInterval I/O tasks (e.g., file reading in Node.js) b) Microtask Queue (Higher Priority) – Contains: Promise.then() and async/await MutationObserver (used in the DOM) How is this all relevant to event loop? Well, think of the event loop as a continuous process JavaScript has, it continuously checks if the call stack is empty and if the queue has any pending tasks. The Overall Process: 1️⃣ The call stack executes all synchronous code. 2️⃣ Once the stack is clear, the event loop checks the microtask queue and runs any pending microtasks. 3️⃣ If the microtask queue is empty, the event loop moves to the callback queue (macrotasks) and executes tasks one by one. 4️⃣ Repeat forever (until the program finishes). Hoistin
After a career break from tech, I found myself going back to the roots of Software Engineering, exploring different areas such as AI and Rust. Along the way, I realised it would be valuable to document my findings and build a guide on how to be "cracked" as a Software Engineer—not just writing code, but truly understanding how things work under the hood.
The reason is simple: with the rise of Generative AI, the barrier to entry for software engineers is lower than ever, but at the same time, the demands on existing engineers are only increasing. I believe we are shifting from an era of specialism to an era of breadth.
Before tools like ChatGPT and Claude, companies hired experts in specific frameworks or tech stacks. Now, learning new technologies has become easier and faster, with automation handling many repetitive tasks. But this means that true engineering knowledge is more important than ever.
Anyone can code. Few can engineer. To stand out, it’s crucial to understand the internals of the technologies we use—how they work, how they scale, and how they solve complex modern-day problems.
With that in mind, let’s begin with JavaScript—the mechanics and magic behind it all.
Understanding JavaScript’s Execution Model
JavaScript is a single threaded programming language. This means it only handles one task at a time, unlike compiled languages such as Rust and C++, it can only execute one operation at a time. Here are some key features of JavaScript to note down
It uses a call stack to keep track of function execution
If we have a nested function - meaning a function that calls another function, it is added to the call stack, and popped off when the function has completed execution
Blocking code (nested for loops or synchronous network calls) freezes everything as JavaScript can’t move forward until it is finished.
So the question maybe, how does JavaScript handle tasks like API calls and timers if it is single threaded? Read through the whole page and you will find out!
The Call Stack – "The Brain of JavaScript Execution"
Before we jump in on how JavaScript handles things like API calls and timers, it is crucial to understand one core aspect of the language. The Call Stack, you can think of it as the “brain” of JavaScript as the subtitle of this section suggests.
But before that, let’s briefly go over what a stack is in the realm of Computer Science.
A stack is a data structure that can contain a list of elements, which has two operations.
Push - adds an element into the Stack
Pop - removes the recently added element from the Stack.
When JavaScript executes a function, it adds it to the call stack, if that function calls another function inside, then that function is added on top of the call stack. Only until the execution is finished is when the function is popped off the stack.
So we have a function named addition, when it is called at the bottom of the script, it is the first one added to the stack, inside the addition function, there’s a function called anotherFunction(), it will be the second one added to the stack, then inside that, there is someOtherFunction() being called, which is the last one added to the stack.
Once someOtherFunction is finished executing, then it will be the first one to be popped off the stack, and the same goes for the other functions when they are finished with execution.
The Event Loop & Async JavaScript – "How JavaScript Never Sleeps"
At the beginning, I asked how JavaScript handles things like API calls if it is single-threaded. Enter the event loop. This is what allows concurrency, it allows non-blocking code by offloading it to the browser or the Node.js runtime environment.
So there’s 3 components to bear in mind
1) The Call Stack - we went over this already in the previous section
2) Web API’s - JavaScript delegates asynchronous tasks (such as setTimeout, fetch, and DOM events) to the Web APIs provided by the browser (or Node.js).
3) Callback Queue & Microtask Queue - Once an async task completes, it can’t execute immediately. Instead, it goes into a task queue and waits for the event loop to process it.
There are two types of queues:
a) Callback Queue (Macrotask Queue) – Contains:
setTimeout
setInterval
I/O tasks (e.g., file reading in Node.js)
b) Microtask Queue (Higher Priority) – Contains:
Promise.then() and async/await
MutationObserver (used in the DOM)
How is this all relevant to event loop?
Well, think of the event loop as a continuous process JavaScript has, it continuously checks if the call stack is empty and if the queue has any pending tasks.
The Overall Process:
1️⃣ The call stack executes all synchronous code.
2️⃣ Once the stack is clear, the event loop checks the microtask queue and runs any pending microtasks.
3️⃣ If the microtask queue is empty, the event loop moves to the callback queue (macrotasks) and executes tasks one by one.
4️⃣ Repeat forever (until the program finishes).
Hoisting – "Variables Before They Exist?"
Hoisting is JavaScript's default behaviour of moving all declarations to the top of the current scope.
This means you can use a function or variable before its declaration without getting a ReferenceError.
If you use a variable before it is defined using the var keyword, you can use it, however it will be logged as undefined, as the declaration var a is hoisted at the top of the scope but the expression = 10, is not.
If you attempt the same thing with let and const, you will get a ReferenceError, as the variable definitions cannot be accessed before initialisation.
The same behaviour can be seen with function declarations and function expressions. Function declarations are fully hoisted, meaning both the function name and body move to the top of the scope. You can access the function before it is defined. Function expressions are not hoisted, so you cannot access them before it is assigned.
The this Keyword – "The Most Misunderstood Keyword in JavaScript"
The this keyword refers to the context where a piece of code is. Usually it is used in object methods. So let me show you an example of this by using the class constructor that JavaScript provides to create objects.
Inside the constructor of the class, we have this.name and this.age being set to the name and age being passed down as arguments. So now at this stage, name and age are set as properties of the Person class, and if you want to reference them anywhere in your code as we are doing it in the introduce() method, you will need to use the this keyword to refer back to the name and age you assigned in the constructor.
Similarly, if you have another method in the class, and you want to call the introduce() method inside it, then you would have to do this.introduce() to refer back to the method that was defined inside the class.
The Prototype Chain – "How JavaScript Inherits Without Classes"
The prototype chain is a mechanism in JavaScript that allows inheritance and property/method lookup. JavaScript in itself is a prototype-based language, meaning objects can inherit properties and methods from other objects.
Key Concepts:
1) Prototype Object: Every object in JavaScript has a hidden [[Prototype]] property (accessible via proto or Object.getPrototypeOf()). This property points to another object, which is its prototype.
2) Prototype Chain: When you access a property or method on an object, JavaScript first checks if the object itself has that property. If not, it looks up the prototype chain by following the [[Prototype]] link until it finds the property or reaches the end of the chain (where [[Prototype]] is null).
3) Constructor Functions: Functions in JavaScript can act as constructors. When you create an object using new, the object’s [[Prototype]] is set to the constructor’s prototype property.
4) Object.prototype: This is the top of the prototype chain. Most objects ultimately inherit from Object.prototype, which provides methods like toString(), hasOwnProperty(), etc
Memory Management & Garbage Collection – "The Hidden Cost of JavaScript"
Garbage collection is a process of automatically freeing up memory by removing objects that are not needed by the program. JavaScript handles memory allocation and deallocation automatically, however Cracked Software Engineers should still be mindful of how memory is used to avoid performance issues
Memory Lifecycle:
Allocation: Memory is allocated when objects, functions, or primitives are created.
Usage: Memory is used when reading or writing to variables.
Release: Memory is released when objects become unreachable and are garbage collected.
Memory Allocation:
Numbers, strings, booleans, etc., are stored directly in memory.
Objects, arrays, and functions are stored in the heap, and references to them are stored in variables.
Conclusion & Takeaways
Now, I have been using JavaScript in my career for almost 4 to 5 years now, it seems easy at first but underneath the hood, there is a lot to know and master to become a Cracked Software Engineer. Truly understanding the internals is what is going to differentiate you from a web developer or a simple coder in the job market. In an era where AI tools like Windsurf and DeepSeek are gaining momentum, truly understanding what is going on underneath is going to differentiate you from the rest.