JavaScript new Keyword: Under the Hood
A practical guide to constructor functions, prototype chains, and how JavaScript builds objects from scratch.

👋 Hey, I'm Mohd Kaif – a student documenting my journey through code. I write about what I'm learning in real-time – the wins, the struggles, and the "aha!" moments. From JavaScript and React to backend systems with Node.js, databases, DevOps, TypeScript, and AI integrations. This blog is my public learning journal: honest, evolving, and always exploring. If you're curious about any of these topics, let's learn and build together!
Most developers type new dozens of times a week without thinking twice. You write new Promise(), new Map(), new Person() — and it just works. Until one day it doesn't, and you're left staring at undefined wondering what went wrong.
Does any of this sound familiar?
You forgot
newin front of a constructor and got backundefinedinstead of an object — and had no idea whyYou've seen
__proto__andprototypein the console and quietly closed the tabYou know
new"creates objects" but couldn't explain what actually happens between the call and the resultYou've used JavaScript classes but feel uneasy knowing you don't understand what's happening under the hood
The problem isn't that you're a bad developer. The new keyword triggers a multi-step process that JavaScript hides completely from you — and most tutorials skip right past it.
If you've ever felt like new is a black box, this article is for you.
✅ What You'll Learn
What the
newkeyword actually does — all four steps, explained clearlyWhy prototype linking matters and how it connects every object you create
How constructor functions work and how they relate to modern ES6 classes
When forgetting
newsilently breaks your code (and how to prevent it)Where this pattern shows up in real libraries like React, and native APIs like
PromiseandMapHow to build your own mental model so JavaScript's object system finally clicks
No advanced prerequisites required — if you know basic JavaScript functions and objects, you're ready.
What Is a Constructor Function?
Before understanding new, you need to understand what it's working with.
A constructor function is just a regular JavaScript function with one convention: it's meant to create and return objects, and its name starts with a capital letter.
function Person(name, age) {
this.name = name;
this.age = age;
}
That's it. There's nothing structurally different about this function — the capital P is purely a signal to other developers (and to yourself) that it should be called with new.
Call it without new, and it behaves like any other function. Call it with new, and JavaScript activates a completely different execution path. That path is what we're here to understand.
The 4-Step Process Behind new
When you write:
const user1 = new Person("Aman", 25);
You might think JavaScript just "fills in" the object. In reality, the engine runs through four distinct steps. Let's walk through each one.
Step 1: A Brand-New Empty Object Is Created
const obj = {};
JavaScript silently creates a fresh, empty object. This object has no properties yet — it's a blank slate that will eventually become user1.
Think of it like a new employee starting their first day with an empty desk. The constructor function is about to set it up.
Step 2: The Object Is Linked to a Prototype
obj.__proto__ = Person.prototype;
This is the step most developers miss — and it's the most important one.
Every function in JavaScript automatically gets a prototype property. When you use new, the newly created object gets secretly linked to that prototype. This means user1 can access any method you define on Person.prototype, even though those methods aren't stored directly on user1 itself.
Think of it like a company handbook. Every employee (instance) gets their own desk (properties), but they all share access to the same handbook (prototype) stored in the break room. The handbook isn't copied for each employee — it lives in one place and everyone references it.
user1 → Person.prototype → Object.prototype → null
This chain — called the prototype chain — is how JavaScript handles inheritance. When you access a property or method, JS walks up this chain until it finds it (or reaches null).
Step 3: this Is Bound to the New Object
Person.call(obj, "Aman", 25);
Now the constructor function runs — but with this pointing to the new empty object from Step 1. So when the constructor executes:
this.name = name; // obj.name = "Aman"
this.age = age; // obj.age = 25
...those properties land directly on obj. After this step, the object looks like:
obj = { name: "Aman", age: 25 }
The employee's desk is now set up.
Step 4: The Object Is Returned
return obj;
If the constructor doesn't explicitly return a different object, JavaScript automatically returns the newly built object. So:
const user1 = new Person("Aman", 25);
// user1 is now { name: "Aman", age: 25 }
The four steps together, visualized:
new Person("Aman", 25)
│
▼
① Create empty object: {}
│
▼
② Link prototype: obj.__proto__ = Person.prototype
│
▼
③ Bind this + run constructor: obj.name = "Aman", obj.age = 25
│
▼
④ Return the object → user1
🏋️ Exercise 1: Trace the Steps Yourself
Take this constructor:
function Car(make, year) {
this.make = make;
this.year = year;
}
const myCar = new Car("Toyota", 2022);
On paper (or in a comment), write out what happens at each of the four steps. Then verify by running console.log(myCar) and console.log(myCar.__proto__ === Car.prototype) in your browser console.
Expected output:
{ make: 'Toyota', year: 2022 }
true
If you got true for the second line, you just confirmed prototype linking with your own eyes. 🎉
Why Prototype Linking Is So Powerful
Now that you know objects get linked to a prototype, here's why that matters in practice.
Imagine you define a greet method directly inside the constructor:
function Person(name) {
this.name = name;
this.greet = function() { // ⚠️ Don't do this
console.log(`Hi, I'm ${this.name}`);
};
}
Every single time you call new Person(), JavaScript creates a brand-new copy of greet and attaches it to that instance. Create 10,000 users and you have 10,000 identical functions sitting in memory.
The fix: define shared methods on the prototype instead.
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() {
console.log(`Hi, I'm ${this.name}`);
};
Now every instance shares the same single greet function via the prototype chain. The instances still behave identically, but the memory footprint is drastically smaller.
const user1 = new Person("Aman");
const user2 = new Person("Riya");
user1.greet === user2.greet // ✅ true — same function, shared via prototype
user1 === user2 // ✅ false — different objects
Key insight: Instances are unique (different objects with different data). Behavior is shared (methods live on the prototype, not on each instance).
🏋️ Exercise 2: Spot the Memory Problem
function Button(label) {
this.label = label;
this.click = function() {
console.log(`${this.label} clicked`);
};
}
const b1 = new Button("Submit");
const b2 = new Button("Cancel");
console.log(b1.click === b2.click); // What does this log?
Run this code, note the output, then refactor it to move click onto Button.prototype. After refactoring, the === check should return true.
What Happens If You Forget new?
This is where things go quietly wrong.
const user1 = Person("Aman", 25); // Missing new!
Without new, none of the four steps run. There's no new object, no prototype linking, and this doesn't point where you expect — it points to the global object (window in browsers, global in Node.js) in non-strict mode, or throws a TypeError in strict mode.
Result:
console.log(user1); // undefined
console.log(window.name); // "Aman" — you just polluted the global scope 😬
Two ways to guard against this:
Option 1 — Use strict mode:
"use strict";
function Person(name) {
this.name = name; // Throws TypeError if called without new
}
Option 2 — Self-correcting constructor:
function Person(name) {
if (!(this instanceof Person)) {
return new Person(name); // Automatically fix the missing new
}
this.name = name;
}
Option 2 is more forgiving — it works whether or not new was used. Option 1 fails loudly, which is actually better in most codebases because it catches bugs early.
One Gotcha: Returning an Object Overrides Everything
Here's a subtle edge case that trips up even experienced developers.
If your constructor explicitly returns an object, that overrides the automatically returned instance:
function Person(name) {
this.name = name;
return { name: "Override" }; // ← returns a plain object
}
const user = new Person("Aman");
console.log(user.name); // "Override" — not "Aman"!
However, returning a primitive (string, number, boolean) is silently ignored — JavaScript still returns the constructed instance:
function Person(name) {
this.name = name;
return 42; // ← returning a primitive is ignored
}
const user = new Person("Aman");
console.log(user.name); // "Aman" — works as expected
The rule: Only returning a non-null object from a constructor overrides the default behavior.
How This Connects to Modern JavaScript
Understanding new isn't just historical knowledge — it directly explains how the features you use every day work under the hood.
ES6 Classes Are Constructor Functions in Disguise
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hi, I'm ${this.name}`);
}
}
This looks like classical object-oriented programming from Java or Python, but JavaScript classes are syntactic sugar over the exact same prototype-based system. Under the hood, greet is placed on Person.prototype, and new Person() still runs the same four-step process.
You can verify this yourself:
console.log(typeof Person); // "function" — it's still a function!
Native APIs You Use Every Day
The same mechanism powers many built-in JavaScript APIs:
const myMap = new Map(); // Creates a Map instance
const mySet = new Set([1, 2, 3]); // Creates a Set instance
const p = new Promise((res) => {}); // Creates a Promise instance
Every one of these runs through the four-step new process. myMap.__proto__ === Map.prototype is true, just like with your own constructors.
React Class Components (Pre-Hooks Era)
class MyComponent extends React.Component {
render() {
return <div>Hello</div>;
}
}
When React renders a class component, it calls new MyComponent(props) internally. React's rendering engine relies on prototype-based inheritance to distinguish class components from function components — which is precisely why the extends React.Component part is required.
🏋️ Exercise 3: Build Your Own new
The best way to truly understand new is to implement it yourself. Here's a skeleton:
function myNew(Constructor, ...args) {
// Step 1: Create an empty object
const obj = ???;
// Step 2: Link its prototype
???.__proto__ = ???;
// Step 3: Run the constructor with `this` bound to obj
const result = ???;
// Step 4: Return obj (unless constructor returned its own object)
return ???;
}
// Test it:
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() {
console.log(`Hi, I'm ${this.name}`);
};
const user = myNew(Person, "Aman");
console.log(user.name); // "Aman"
user.greet(); // "Hi, I'm Aman"
Hint for Step 2: Use Object.create(Constructor.prototype) instead of manually setting __proto__ — it's the modern, spec-compliant way to do prototype linking.
If your myNew passes both assertions, you've fully internalized how new works. Completing this exercise is worth more than reading ten articles about it.
Your Mental Model: Think Like the Engine
Whenever you see new in code from now on, mentally translate it like this:
const x = new Foo(arg1, arg2);
① {} — create empty object
② obj.__proto__ = Foo.prototype — link to prototype chain
③ Foo.call(obj, arg1, arg2) — run constructor, bind this
④ return obj — hand back the finished instance
Internalize these four steps and a whole layer of JavaScript complexity dissolves:
| Concept | How it connects to new |
|---|---|
| ES6 Classes | Syntax sugar over constructor + prototype |
this confusion |
this is the new object created in Step 1 |
| Prototype chain | Established in Step 2 — always |
instanceof operator |
Checks if Constructor.prototype is in the chain |
Object.create() |
Manually does Step 2 without running a constructor |
What to Learn Next
Now that new makes sense, here's where to go to deepen your understanding:
Prototype Chain & Inheritance — Understand how
Object.create()works and how to build multi-level inheritance without classes. This is the foundation for everything in JavaScript's object model.thisin JavaScript —newis just one of four waysthisgets bound. Learn explicit binding (.call(),.apply(),.bind()), implicit binding, and arrow functions to masterthiscompletely.ES6 Classes In Depth — Now that you understand what classes compile down to, explore
extends,super(), static methods, and private fields with full confidence.Object Composition vs. Inheritance — Once you're comfortable with prototypes, explore why many experienced developers prefer composition over classical inheritance — and how patterns like mixins offer a powerful alternative.
💬 Got Questions?
Drop a comment below! I'd love to hear where this clicked for you, or where you're still feeling uncertain — both are worth discussing.
Here are topics coming up in future articles:
The Prototype Chain, Visualized: A deep dive into
__proto__,Object.prototype, and whynullis at the top of every chainMastering
thisin JavaScript: All four binding rules explained with real bugs and their fixesObject.create()vsnewvs Class Syntax: When to use each and what tradeoffs you're makingJavaScript Inheritance Without the Confusion: How to use
extendsandsupercorrectly, and when composition is the better choice
Found this helpful? Share it with a developer who's ever been confused by new, prototype, or this — that's most of us at some point. And if you spotted an error or have a question, the comments are open.
Happy coding! 🚀




