Dec 01, 2022Last modified July 8, 2025
Javascript Deep Dive
Even after working on javascript for a few years, I still feel like I am learning new things about JS. I have been reading the YDKJS book series and there are some really good insights (learnings) in the book which I am going to share in this article.
I did refer other internet resources to understand some of the concepts. So there are few pointers which didnt come from the book. Additionally, as I started deep diving on different concepts, I found few more concepts which I didnt understand fully. So I have added some notes from that exploration as well.
Javascript Engine and Runtime
- Javascript engine runs as a part of javascript runtime, which is generally a browser or a node js server.
- There are several JS engines available, the most famous one is the v8 engine, implemented inside Chrome browser and Node.js, but there is also Spidermonkey (used by Firefox) or Chakra (used by IE and Edge).
- All JS engines are implemented to follow the ECMAScript standard. Thats why they all behave in same way.
- JS engine is responsible for reading the JS code, tokenizing & parsing it, then ccompiling it into bytecode and finally executing it.
- It is single-threaded, meaning that it executes code sequentially, one statement at a time, within a single thread of execution (in the browser it could be a tab for example, so each tab has an instance of the JavaScript engine it implements).
- It includes two main components, the call stack and the heap (see image below). The call stack is a data structure that keeps track of the function that is being executed and of the subsequent ones.
- The heap contains information such as the functionβs arguments, local variables and the location in the code where the function was called. The heap instead is a dynamic allocated region of memory that stores the address (not the value!) of objects, arrays and other data structures created during a function execution.

Scopes
- Scopres are essentially visibility rules which dictate what part of your program can acccess which variables.
- Whenever you declare a variable in JS and initialize it, the compiler first creates the variable in memory and assigns a scope to it. During execution, the JS engine will look up the variable in the scope chain and then assigns the value.
- There are two types of lookups which generally happen in JS during execution - LHS lookup and RHS lookup.
- LHS lookup happens for the variable who we are assigning the value to. RHS lookup happens for the variable which we are trying to access.
- JavaScript needs rules to decide which name variable to use when there are multiple ones! These specific rules are called scope.
- Scopes can be nested. Whenver a particular variable is not found in the current scope, the engine looks up the scope chain until it finds the variable or reaches the global scope.
- Lexical scope - Javascript scopes are lexical scopes. Meaning during parsing/compilation phase, the compiler will look at the location where the variables and functions are declared and then create scope rules for them accordingly. Lexical scope = static scope.
- You can modify scopes using the eval function and with keyword.
- eval() function takes a string as an argument and executes it as if it were a program. Evals are discouraged due to security concerns related to code injection attacks. Many sites use CSPs (Content Security Policies) to prevent eval() from being used.
- The with keyword is used to create a scope. It takes an object as an argument and then all the properties of the object are added to the scope.
- Originally there were two main types of scopes - global scope and function scope.global and function. Global scope is the outermost scope and function scope is the scope created by a function. ES6 introduced block scope with the let and const keywords.
- Using scopes allows to facilitate collision avoidance as well. This happens when two or more identifiers with the same name but different intended usages are declared.
- Hoisting - Hoisting is a mechanism in JavaScript where variable and function declarations are moved to the top of their containing scope during the compilation phase, before the code is executed. This is what allows you to use variables and functions before they are declared in your code.
- Function declarations are hoisted first and then variables.
Closures
- Closure is when a function is able to remember and access its lexical scope even when that function is executing outside its lexical scope.
this
- In javascript, the value of this is determined by the call site. Call site simply means the location in the code where a function is called.
- Four rules for determining the value of this :
- Default binding - by default this is set to the global (in node js) / window (in browser) object
- New binding - when a function is called with the new keyword, the value of this is set to the newly created object.
- Implicit binding - If a function is called on an objet, obj.foo(), this will be the object it was called on.
- Explicit binding - When a function is called with the call or apply method, this is explicitly set to the first argument passed to call or apply.
- Precedence of binding rules - new binding > explicit binding > implicit binding > default binding.
- bind function does not work on arrow functions. It executes only once, the first time it is called.
- In react when we pass a callback from one component to another, the value of this is lost. This is because when we assign a function to a variable, it loses the context of the original object.
Prototypical Inheritance
Good artice on Prototypical Inheritance
- Inheritance in javascript basically means linking objects to other objects.
- Every function has a prototype property. Functions get a prototype property because they might be used as constructors, but regular objects don't need one because they're not meant to create other objects.
- Only function declarations and function expressions have the prototype property.
- JavaScript uses an inheritance model called βdifferential inheritanceβ. What that means is that methods arenβt copied from parent to child. Instead, children have an βinvisible linkβ back to their parent object.
Objects
- Object.freeze() - prevents modification of existing property values and prevents the addition of new properties.
- Object.seal() - prevents new properties from being added, but allows modification of existing properties.
- Object.preventExtensions() - prevents new properties from being added, but allows modification of existing properties.
Asynchronous Javascript
- Javascript code can be thought of in two ways - one of "Now" type and other of "Later" type. The "Now" type is the synchronous code that is executed immediately. The "Later" type is the asynchronous code that is executed later.
- The event loop is how JavaScript achieves non-blocking behavior. Long-running operations (like waiting for a network request via fetch) are handled by the environment APIs, freeing the Call Stack.
- setTimeout is part of Web API. When the JS engine encounters setTimeout, it puts the callback function on the taskQueue and hands over the control to browser to execute the setTimetout function.
- The JS event loop keep track of the taskQueue and the callStack. When the callStack is empty, the event loop takes the first task from the taskQueue and pushes it to the callStack.
- A callback is a function that is passed as an argument to another function. The intention is that the receiving function will execute (βcall backβ) your function at some point in the future β the βLaterβ part of our execution model.
- When you pass your callback function (like taskForLater or handleUser) to another utility (like setTimeout or ajax), you are inverting the control. Instead of your code calling the function directly when you decide, you are handing control over to the other party, trusting them to execute your function correctly.
- The Event Loop constantly cycles through these parts:
- It checks if the Call Stack is empty. If itβs not, it keeps processing whatever is on the stack (βNowβ).
- Once the Call Stack is empty, it checks the Microtask Queue. If there are any tasks (jobs) waiting, it executes all of them, one by one, until the Microtask Queue is empty. If processing a microtask adds more microtasks, those are also processed in the same cycle.
- Only when the Microtask Queue is empty does it check the Task Queue (Macrotask Queue).
- If the Task Queue has a task, it takes the oldest one, pushes its callback function onto the (now empty) Call Stack, and executes it (βLaterβ).
- (In browsers) After processing microtasks and potentially one macrotask, the loop may perform rendering updates if needed.
- The loop then repeats.

async/await
- A Promise isnβt just a callback mechanism; itβs a placeholder for a future value.
- Both async and await are keywords in javascript.
- When we add async keyword to a function, it does two things:
- It makes the function return a promise. If a function returns a value, it is first wrapped in a resolved promise and then returned. If a functioin throws an error, it is wrapped in a rejected promise and then thrown.
- It enables the use of await keyword in the function.
- await keyword can only be used inside an async function.
- await keyword makes the function wait for the result of the promise. If the promise is resolved, the result is returned. If the promise is rejected, the error is thrown.
- Both async and await make the code look synchronous. It makes the code easier to debug and read.
Currying
- Currying refers to the process of taking a function with n arguments and transforming it into n functions that each take a single argument. It essentially creates a chain of partially applied functions that eventually resolves with a value.
- Here's a code snippet of a curry function which takes a function with n arguments and transforms it into n functions that each take a single argument.
- Explanation of the code: