Skip to main content

Command Palette

Search for a command to run...

JavaScript new Keyword: Under the Hood

A practical guide to constructor functions, prototype chains, and how JavaScript builds objects from scratch.

Updated
JavaScript new Keyword: Under the Hood
M

👋 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 new in front of a constructor and got back undefined instead of an object — and had no idea why

  • You've seen __proto__ and prototype in the console and quietly closed the tab

  • You know new "creates objects" but couldn't explain what actually happens between the call and the result

  • You'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 new keyword actually does — all four steps, explained clearly

  • Why 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 new silently breaks your code (and how to prevent it)

  • Where this pattern shows up in real libraries like React, and native APIs like Promise and Map

  • How 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:

  1. 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.

  2. this in JavaScriptnew is just one of four ways this gets bound. Learn explicit binding (.call(), .apply(), .bind()), implicit binding, and arrow functions to master this completely.

  3. ES6 Classes In Depth — Now that you understand what classes compile down to, explore extends, super(), static methods, and private fields with full confidence.

  4. 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 why null is at the top of every chain

  • Mastering this in JavaScript: All four binding rules explained with real bugs and their fixes

  • Object.create() vs new vs Class Syntax: When to use each and what tradeoffs you're making

  • JavaScript Inheritance Without the Confusion: How to use extends and super correctly, 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! 🚀

Zero to Full Stack Developer: From Basics to Production

Part 28 of 50

Complete full-stack web development series from zero to production. Learn HTML, CSS, JavaScript, TypeScript, React, Next.js, Node.js, databases, Docker, AWS, and AI integration. Build real-world projects step-by-step.

Up next

`this` in JavaScript: The Complete Guide

Stop guessing what 'this' refers to. Master calling context, arrow functions, ES6 classes, 'call', 'apply', and 'bind' — with hands-on exercises.