Skip to main content

Command Palette

Search for a command to run...

The new Keyword in JavaScript: How Objects Are Created

Updated
14 min read
The new Keyword in JavaScript: How Objects Are Created

Who is this for? If you have seen new in JavaScript code and wondered what it actually does behind the scenes — or if you want to understand how objects are built from constructor functions — this guide explains everything step by step.


Table of Contents

  1. What Does the new Keyword Do?

  2. Constructor Functions

  3. The Object Creation Process

  4. How new Links Prototypes

  5. Instances Created from Constructors

  6. Diagrams

  7. Quick Reference


Introduction

When you need multiple objects with the same structure — say, ten users, each with a name, email, and role — you could write each one by hand:

const user1 = { name: "Priya",  email: "priya@example.com",  role: "admin" };
const user2 = { name: "Rahul",  email: "rahul@example.com",  role: "member" };
const user3 = { name: "Ananya", email: "ananya@example.com", role: "member" };
// ... and seven more

This works, but it is repetitive, hard to maintain, and there is no connection between these objects — no shared behaviour, no common blueprint.

JavaScript gives you a better way: constructor functions and the new keyword. You define the shape of an object once, and then use new to stamp out as many instances as you need.

💡 Open your browser console (F12 → Console tab) and try every example as you read.


1. What Does the new Keyword Do?

new is a special keyword that, when placed in front of a function call, transforms that function into an object factory. It handles the repetitive setup work — creating an empty object, linking it, and returning it — automatically.

Without new, a function just runs and returns whatever it explicitly returns (or undefined). With new, four things happen automatically:

1. A brand new empty object is created
2. `this` inside the function is set to that new object
3. The new object's prototype is linked to the constructor's prototype
4. The new object is automatically returned

The difference in one example

function greet() {
  console.log("Hello!");
}

// Without new — just calls the function normally
greet(); // "Hello!"

// With new — creates and returns a new object
const result = new greet();
console.log(result); // greet {}  ← an empty object was created and returned

This does not look useful yet — because greet was not designed to be a constructor. Let us look at functions that are.


2. Constructor Functions

A constructor function is a regular function written with the intent to be called with new. By convention, constructor functions are named with a capital first letterUser, Car, Product — to signal to other developers: "call this with new."

Inside a constructor function, you use this to attach properties to the object being created.

Your first constructor function

function User(name, email, role) {
  this.name  = name;
  this.email = email;
  this.role  = role;
}

There is no return statement. There is no manual object creation. You just describe what properties the new object should have, using this as a reference to the object being built.

Creating objects with new

const user1 = new User("Priya",  "priya@example.com",  "admin");
const user2 = new User("Rahul",  "rahul@example.com",  "member");
const user3 = new User("Ananya", "ananya@example.com", "member");

console.log(user1);
// User { name: 'Priya', email: 'priya@example.com', role: 'admin' }

console.log(user2.name);  // "Rahul"
console.log(user3.email); // "ananya@example.com"

Each call to new User(...) produces a completely separate object, but they all share the same structure defined by the constructor.

Adding methods to a constructor

You can add methods — functions as properties — directly inside the constructor:

function Car(brand, model, year) {
  this.brand = brand;
  this.model = model;
  this.year  = year;

  this.describe = function() {
    return this.year + " " + this.brand + " " + this.model;
  };
}

const car1 = new Car("Toyota", "Fortuner", 2022);
const car2 = new Car("Honda",  "City",     2020);

console.log(car1.describe()); // "2022 Toyota Fortuner"
console.log(car2.describe()); // "2020 Honda City"

⚠️ A note on methods in constructors: Putting methods directly inside the constructor means each new object gets its own copy of that function — which wastes memory if you create many instances. The better approach is to add methods to the prototype (covered in section 4).

What happens if you call a constructor without new?

function User(name, email) {
  this.name  = name;
  this.email = email;
}

// ❌ Without new — this refers to the global object (or undefined in strict mode)
const user = User("Priya", "priya@example.com");
console.log(user);       // undefined — nothing was returned
console.log(window.name); // "Priya" — accidentally set on the global object!

This is one of the most common beginner mistakes. Always use new when calling a constructor function. The capital letter naming convention is there precisely to remind you.


3. The Object Creation Process

When you write new User("Priya", "priya@example.com", "admin"), here is exactly what JavaScript does — step by step:

Step 1 — A new empty object is created

// JavaScript secretly does this first:
const obj = {};

A plain, empty object with no properties is created in memory.

Step 2 — this is pointed at the new object

// JavaScript sets:
this = obj; // now `this` inside the constructor refers to this new object

Every time you write this.name = name inside the constructor, you are writing a property onto this new object.

Step 3 — The constructor function body runs

function User(name, email, role) {
  this.name  = name;   // obj.name  = "Priya"
  this.email = email;  // obj.email = "priya@example.com"
  this.role  = role;   // obj.role  = "admin"
}

The constructor runs line by line, attaching each property to the object via this.

Step 4 — The new object is automatically returned

// JavaScript secretly does this last:
return this; // returns the fully built object

You do not write returnnew handles it. The fully populated object is returned and assigned to your variable.

Visualising all four steps together

new User("Priya", "priya@example.com", "admin")

Step 1:  {}                              ← empty object created
Step 2:  this → {}                       ← this points to it
Step 3:  this.name  = "Priya"
         this.email = "priya@example.com"
         this.role  = "admin"
         → { name: "Priya", email: "priya@example.com", role: "admin" }
Step 4:  return this                     ← object returned automatically

Result:  user1 = { name: "Priya", email: "priya@example.com", role: "admin" }

What if you add your own return to a constructor?

function User(name) {
  this.name = name;
  return { message: "I overrode the return!" }; // returning an object
}

const user = new User("Priya");
console.log(user); // { message: "I overrode the return!" }
// The name property is lost — your return replaced the automatic one

If a constructor explicitly returns an object, that object replaces the automatic return. If it returns a primitive (number, string, boolean), the return is ignored and the constructed object is used as normal. In practice, you should never add a return to a constructor function.


This is the most important — and most misunderstood — part of what new does.

What is a prototype?

Every function in JavaScript has a special property called prototype. It is just an object. When you create instances with new, those instances are linked to this prototype object.

This link lets all instances share methods and properties without each one having its own copy — which saves memory and keeps your code DRY.

function User(name, email) {
  this.name  = name;
  this.email = email;
}

// Instead of putting greet inside the constructor,
// add it to the prototype — all instances will share it
User.prototype.greet = function() {
  return "Hi, I am " + this.name + "!";
};

User.prototype.getEmail = function() {
  return "Contact me at: " + this.email;
};

Now every User instance automatically has access to greet and getEmail:

const user1 = new User("Priya",  "priya@example.com");
const user2 = new User("Rahul",  "rahul@example.com");

console.log(user1.greet());    // "Hi, I am Priya!"
console.log(user2.greet());    // "Hi, I am Rahul!"
console.log(user1.getEmail()); // "Contact me at: priya@example.com"

Neither user1 nor user2 has a greet property of their own. When you call user1.greet(), JavaScript looks for greet on user1, does not find it, then looks up the prototype chain and finds it on User.prototype. This lookup is automatic.

const user1 = new User("Priya", "priya@example.com");

// new does this automatically:
// user1.__proto__ === User.prototype  → true

console.log(Object.getPrototypeOf(user1) === User.prototype); // true

Every instance created with new User() has an internal link — [[Prototype]] — pointing to User.prototype. This is what allows shared methods to work.

Methods on prototype vs methods on the instance

function Car(brand) {
  this.brand = brand;

  // BAD — each instance gets its own copy of this function
  this.honk = function() { return this.brand + " goes beep!"; };
}

// GOOD — one copy shared by all instances via prototype
Car.prototype.honk = function() {
  return this.brand + " goes beep!";
};

const car1 = new Car("Toyota");
const car2 = new Car("Honda");

// With prototype approach:
console.log(car1.honk === car2.honk); // true — they share the same function
// With constructor approach:
// car1.honk === car2.honk would be false — different copies

With 1000 Car instances, the prototype approach stores honk once. The constructor approach stores 1000 copies. The prototype is always the right choice for methods.


5. Instances Created from Constructors

An instance is any object created using new with a specific constructor. Each instance:

  • Has its own unique property values (name, email, etc.)

  • Shares methods from the constructor's prototype

  • Knows which constructor created it (via instanceof)

Checking if an object is an instance

function User(name) { this.name = name; }
function Car(brand) { this.brand = brand; }

const user1 = new User("Priya");
const car1  = new Car("Toyota");

console.log(user1 instanceof User); // true
console.log(user1 instanceof Car);  // false
console.log(car1  instanceof Car);  // true
console.log(car1  instanceof User); // false

instanceof checks the prototype chain to determine which constructor was responsible for creating an object.

Each instance is independent

function Counter(start) {
  this.count = start;

  this.increment = function() {
    this.count++;
  };
}

const counterA = new Counter(0);
const counterB = new Counter(100);

counterA.increment();
counterA.increment();
counterA.increment();

counterB.increment();

console.log(counterA.count); // 3  ← independent
console.log(counterB.count); // 101 ← independent

Changing one instance never affects another. Each object has its own copy of count.

A complete example — putting it all together

function Product(name, price, inStock) {
  this.name    = name;
  this.price   = price;
  this.inStock = inStock;
}

// Shared methods on the prototype
Product.prototype.getDetails = function() {
  return this.name + " — ₹" + this.price;
};

Product.prototype.checkAvailability = function() {
  if (this.inStock) {
    return this.name + " is available.";
  } else {
    return this.name + " is out of stock.";
  }
};

// Creating instances
const laptop = new Product("Laptop",  75000, true);
const phone  = new Product("Phone",   25000, false);
const tablet = new Product("Tablet",  40000, true);

// Each instance uses its own properties
console.log(laptop.getDetails());          // "Laptop — ₹75000"
console.log(phone.getDetails());           // "Phone — ₹25000"
console.log(laptop.checkAvailability());   // "Laptop is available."
console.log(phone.checkAvailability());    // "Phone is out of stock."

// Checking instances
console.log(tablet instanceof Product);   // true
console.log(Object.getPrototypeOf(laptop) === Product.prototype); // true

Verifying the four-step process on a real object

function Animal(name, sound) {
  this.name  = name;
  this.sound = sound;
}

Animal.prototype.speak = function() {
  return this.name + " says " + this.sound + "!";
};

const dog = new Animal("Dog", "Woof");
const cat = new Animal("Cat", "Meow");

// Own properties (set by constructor)
console.log(dog.hasOwnProperty("name"));  // true
console.log(dog.hasOwnProperty("sound")); // true

// Prototype method (NOT owned by the instance)
console.log(dog.hasOwnProperty("speak")); // false ← lives on prototype

// But the method still works via the prototype chain
console.log(dog.speak()); // "Dog says Woof!"
console.log(cat.speak()); // "Cat says Meow!"

Diagrams

Constructor → Instance Creation Flow

┌─────────────────────────────────────────────────────────────┐
│          CONSTRUCTOR → INSTANCE CREATION FLOW               │
│                                                             │
│  function User(name, email) {                               │
│    this.name  = name;                                       │
│    this.email = email;                                      │
│  }                                                          │
│                                                             │
│       new User("Priya", "priya@example.com")                │
│                         │                                   │
│         ┌───────────────┴───────────────────┐               │
│         │        4-STEP PROCESS             │               │
│         │                                   │               │
│  Step 1 │  {}  ← create empty object        │               │
│         │   ↓                               │               │
│  Step 2 │  this → {}  ← link this to it     │               │
│         │   ↓                               │               │
│  Step 3 │  this.name  = "Priya"             │               │
│         │  this.email = "priya@example.com" │               │
│         │   ↓                               │               │
│  Step 4 │  return this  ← auto return       │               │
│         └───────────────┬───────────────────┘               │
│                         ↓                                   │
│   user1 = { name: "Priya", email: "priya@example.com" }     │
│                                                             │
│  Call again → completely new, independent object            │
│  new User("Rahul", "rahul@example.com")                     │
│   → user2 = { name: "Rahul", email: "rahul@example.com" }   │
└─────────────────────────────────────────────────────────────┘

Prototype Linking Visual

┌─────────────────────────────────────────────────────────────┐
│                 PROTOTYPE LINKING VISUAL                    │
│                                                             │
│  function User(name) { this.name = name; }                  │
│  User.prototype.greet = function() { ... };                 │
│                                                             │
│                  ┌─────────────────┐                        │
│                  │  User.prototype  │                        │
│                  │  ─────────────  │                        │
│                  │  greet: fn()    │                        │
│                  │  getEmail: fn() │                        │
│                  └────────┬────────┘                        │
│                           │ [[Prototype]] link               │
│              ┌────────────┴────────────┐                    │
│              ↓                         ↓                    │
│  ┌─────────────────┐       ┌─────────────────┐              │
│  │     user1       │       │     user2       │              │
│  │  ─────────────  │       │  ─────────────  │              │
│  │  name: "Priya"  │       │  name: "Rahul"  │              │
│  │  email: "p@..."  │       │  email: "r@..."  │             │
│  └─────────────────┘       └─────────────────┘              │
│                                                             │
│  user1.greet() → not found on user1                         │
│               → look up to User.prototype                   │
│               → found! greet() runs with this = user1       │
│                                                             │
│  ✦ Own properties live on each instance (name, email)       │
│  ✦ Shared methods live on the prototype (greet, getEmail)   │
│  ✦ One copy of each method, shared by all instances         │
│  ✦ instanceof checks this prototype link                    │
└─────────────────────────────────────────────────────────────┘

Quick Reference

// ─── Define a constructor ─────────────────────────────────
function Person(name, age) {
  this.name = name;    // own property — unique per instance
  this.age  = age;     // own property — unique per instance
}

// ─── Add shared methods via prototype ─────────────────────
Person.prototype.greet = function() {
  return "Hi, I am " + this.name;
};

Person.prototype.isAdult = function() {
  return this.age >= 18;
};

// ─── Create instances ─────────────────────────────────────
const p1 = new Person("Priya", 25);
const p2 = new Person("Rahul", 16);

// ─── Access own properties ────────────────────────────────
console.log(p1.name);       // "Priya"
console.log(p2.age);        // 16

// ─── Access prototype methods ─────────────────────────────
console.log(p1.greet());    // "Hi, I am Priya"
console.log(p2.isAdult());  // false

// ─── Check instance relationship ──────────────────────────
console.log(p1 instanceof Person);                           // true
console.log(Object.getPrototypeOf(p1) === Person.prototype); // true

// ─── Own vs prototype property ────────────────────────────
console.log(p1.hasOwnProperty("name"));  // true  ← own
console.log(p1.hasOwnProperty("greet")); // false ← on prototype

Wrapping Up

The new keyword is one of the most important concepts in JavaScript object-oriented programming. Here is everything you learned:

  • new transforms a regular function into an object factory — running four automatic steps

  • Constructor functions use this to describe what properties each new object should have — no explicit return needed

  • The four steps of new: create empty object → link this → run constructor body → auto return

  • new links the new object's prototype to the constructor's .prototype — enabling shared methods across all instances

  • Instances are independent objects — each with its own properties, but sharing prototype methods

  • Use instanceof to check which constructor created an object

  • Always put methods on the prototype, not inside the constructor — one shared copy is better than one per instance

Once you understand this, JavaScript's class syntax (introduced in ES6) will look very familiar — it is simply a cleaner way to write exactly what you learned here, built on the same constructor and prototype mechanism under the hood.

Happy coding! 🚀


Next step: Look into ES6 class syntax — it uses the same new keyword and prototype system you just learned, just with cleaner, more readable syntax.