Skip to main content

Command Palette

Search for a command to run...

The New Keyword

What Actually Happens When You Write new Person()

Published
7 min read
The New Keyword

You see new everywhere. new Date(). new Map(). new Promise(). You write it, it works, and you move on. But at some point you start wondering what it actually does. Calling a function with new in front behaves nothing like calling a function without it, and that gap is worth understanding.

Here is the short version: new runs a function in a special mode where that function builds and returns an object. The function is called a constructor. The object it builds is called an instance. That is the whole idea. The rest is just details about how it works.

A constructor is not a special kind of function. It is a regular function that you call with new. The convention is to capitalize the first letter so people know it is meant to be called that way, but JavaScript does not enforce this.

A Constructor Function

Before classes came along in ES6, this is how you made objects that shared a structure. You wrote a plain function, used this inside it, and called it with new. It still works exactly the same way today.

// Capital P signals: call this with new
function Person(name, age) {
  this.name = name;
  this.age  = age;
}

const priya = new Person("Priya", 28);
const rohan = new Person("Rohan", 34);

console.log(priya.name); // "Priya"
console.log(rohan.age);  // 34

Each call to new Person() produces a separate object. Priya's name and Rohan's name live on different objects. Changing one does not touch the other. That sounds obvious, but it is worth saying because it is the point.

What new Does

When JavaScript sees new Person("Priya", 28), it does four things in order. You never see any of this happen, but it all runs before your function body even starts.

A blank object is created

JavaScript makes a fresh, empty object. Nothing in it yet. This is what will eventually come back from the call.

**
The prototype is linked**
That blank object gets its internal [[Prototype]] set to Person.prototype. This is how instances share methods without each one getting its own copy. More on this in a moment.

The function runs with this = that object

The constructor body runs, and every time you write this.something, you are writing onto that blank object from step 1.

The object is returned

Unless your constructor explicitly returns a different object, JavaScript returns the one it built in step 1. This is automatic. You do not write return this.

Where Methods Go: The Prototype

Here is something that trips people up early on. If you put a method inside the constructor body using this.greet = function() {...}, every single instance gets its own copy of that function. One hundred Person objects means one hundred separate greet functions sitting in memory, all doing the same thing.

The fix is to put shared methods on Person.prototype instead. Every instance automatically has access to it through the prototype chain, but only one copy exists.

// method in wrong places
function Person(name) {
  this.name = name;

  // Bad: every new Person() gets its own copy of this function
  this.greet = function() {
    console.log("Hi, I'm " + this.name);
  };
}
// method on the prototype
function Person(name) {
  this.name = name; // own property — each instance needs its own
}

// Defined once, shared by every Person instance
Person.prototype.greet = function() {
  console.log("Hi, I'm " + this.name);
};

const priya = new Person("Priya");
const rohan = new Person("Rohan");

priya.greet(); // "Hi, I'm Priya"
rohan.greet(); // "Hi, I'm Rohan"

// Same function object, just this changes per call
console.log(priya.greet === rohan.greet); // true

Own properties live on the instance. Methods that do not need unique per-instance data belong on the prototype. That line is where most of the judgment calls happen.

How Prototype Lookup Works

When you write priya.greet(), JavaScript looks for greet on priya itself first. It is not there. So it follows the internal [[Prototype]] link up to Person.prototype and looks there. It finds it. Done.

If it were not there either, it would keep climbing: Object.prototype is next. If it is not there, you get undefined. This chain of lookups is called the prototype chain, and it is the mechanism behind every method you call on any object in JavaScript.

The practical upshot: methods on the prototype behave exactly like methods on the instance from the caller's perspective. You write priya.greet() either way. The lookup is invisible. You only notice it when you start asking questions like "where does .toString() come from on every object ever."

Forgetting new: What Goes Wrong

Call a constructor without new and JavaScript just runs it as a regular function. No blank object gets created. this inside it is not a fresh instance, it is either the global object (in non-strict mode) or undefined (in strict mode). In non-strict mode you get no error. You just silently write properties onto window and wonder why nothing is working.

function Person(name) {
  this.name = name;
}

const oops = Person("Priya"); // no new

console.log(oops);        // undefined: function returned nothing
console.log(window.name);  // "Priya": oops, now it's global

In strict mode this at least throws a TypeError immediately, which is easier to catch. If you are writing constructor functions today, putting "use strict" at the top of your file is a reasonable safety net.

ES6 classes solve this permanently. A class constructor throws a TypeError if you call it without new, no matter what. That is one of the reasons classes exist.

Classes Do The Same Thing, With Better Guardrails

ES6 classes are not a different object model. They are a cleaner syntax over the same constructor function and prototype setup. If you understand constructors and prototypes, you already understand what classes compile down to.

//  constructor function
function Person(name, age) {
  this.name = name;
  this.age  = age;
}
Person.prototype.greet = function() {
  console.log("Hi, I'm " + this.name);
};

// class 
class Person {
  constructor(name, age) {
    this.name = name;
    this.age  = age;
  }
  greet() {
    console.log("Hi, I'm " + this.name);
  }
}

// Both produce instances with the same shape
const p = new Person("Priya", 28);
p.greet(); // "Hi, I'm Priya"

With the class syntax, greet is automatically placed on Person.prototype. You do not write that out manually. The engine handles it. The result is identical to the constructor function version, but there is less surface area for mistakes.

Constructor vs Instance

Properties and methods can live in three places: on the instance, on the prototype, or on the constructor function itself. Each one is used differently.

function Person(name) {
  this.name = name; // instance property: unique per object
}

Person.prototype.greet = function() { // prototype method: shared
  console.log(this.name);
};

Person.count = 0; // static: lives on the constructor, not instances

const p = new Person("Priya");
p.greet();               // works. found on prototype
console.log(p.count);    // undefined. instances don't see static props
console.log(Person.count); // 0. access it through the constructor

If you want to test what you just read, try this: write a Counter constructor that stores a count on this, then add increment and reset methods on the prototype. Make two counters and verify they count independently. Then check that both counters share the same increment function reference.

That small exercise covers everything in this post. Own properties, shared methods, separate instances. Once that clicks, classes will feel like a notation change rather than a concept change, because you will already know what the class body is doing behind the scenes.

REFERENCES:

Simply JavaScript

Part 3 of 25

JavaScript is a quirky language. To master it, one should know to avoid its hidden traps along with its logic. This series showcase my journey through JS: the pain points, the breakthroughs, and the coding standards that I adopted from my mentors.

Up next

Destructuring in JS

Clean, Concise Data Extraction in Modern JavaScript