Skip to main content

Command Palette

Search for a command to run...

The new Keyword in JavaScript

The Magic of 'new': JavaScript's Object Factory 🏭

Updated
β€’10 min readβ€’View as Markdown
J

Turning chai into code and ideas into full-stack applications. Sharing lessons from my development journey, one commit at a time.

Last Diwali, my little cousin asked me to help make cookies. She handed me a star-shaped cookie cutter and said, "Make a hundred of these."

I pressed the cutter into the dough. Star cookie #1.
I pressed again. Star cookie #2.
Again. Star cookie #3.

Each cookie was unique (slightly different sizes, imperfect edges), but they all came from the same blueprintβ€”the cookie cutter.

As I made cookie #47, it hit me: This is exactly what the new keyword does in JavaScript.

  • Cookie cutter = Constructor function

  • Dough = Memory

  • Pressing the cutter = Using new

  • Each cookie = An object instance

That moment, covered in flour, I finally understood object-oriented programming.


What Does new Actually Do? πŸ€”

The new keyword is JavaScript's way of creating object instances from a blueprint. Here's the simple version:

// Constructor function (the blueprint)
function Cookie(shape, flavor) {
  this.shape = shape;
  this.flavor = flavor;
}

// Using 'new' to create instances
const cookie1 = new Cookie("star", "chocolate");
const cookie2 = new Cookie("heart", "vanilla");

console.log(cookie1); // Cookie { shape: 'star', flavor: 'chocolate' }
console.log(cookie2); // Cookie { shape: 'heart', flavor: 'vanilla' }

Each cookie is a separate object, but both were created using the same blueprint.


Without new: The Confusion 🚫

What happens if you forget new? Let me show you the chaos:

function Cookie(shape, flavor) {
  this.shape = shape;
  this.flavor = flavor;
}

// WITHOUT 'new'
const cookie = Cookie("star", "chocolate");

console.log(cookie); // undefined
console.log(window.shape); // "star" 😱 (global variable!)
console.log(window.flavor); // "chocolate" 😱 (global variable!)

What just happened?!

When you call Cookie() without new:

  • this refers to the global object (window in browsers)

  • The function returns undefined (no explicit return)

  • You just polluted the global scope! πŸ’€

With new:

// WITH 'new'
const cookie = new Cookie("star", "chocolate");

console.log(cookie); // Cookie { shape: 'star', flavor: 'chocolate' } 
console.log(window.shape); // undefined (safe!) 

The 4 Steps of new: Behind the Magic 🎩

When you use new, JavaScript does four things automatically:

function Cookie(shape, flavor) {
  // STEP 1: Create a new empty object
  // (JavaScript does: const this = {};)
  
  // STEP 2: Set the prototype
  // (JavaScript does: this.__proto__ = Cookie.prototype;)
  
  // STEP 3: Execute the constructor function
  this.shape = shape;
  this.flavor = flavor;
  
  // STEP 4: Return the new object
  // (JavaScript does: return this;)
}

const cookie = new Cookie("star", "chocolate");

Let's break down each step:

Step 1: Create Empty Object

// What JavaScript does internally:
const this = {};

A brand new empty object is created in memory.


// What JavaScript does internally:
this.__proto__ = Cookie.prototype;

The new object's internal prototype is linked to the constructor's prototype. This enables inheritance! (More on this later.)


Step 3: Execute Constructor

// Your code runs:
this.shape = shape;      // Adds properties
this.flavor = flavor;    // Adds properties

The constructor function body executes, and this refers to the new object.


Step 4: Return Object

// What JavaScript does internally:
return this;

The newly created object is returned automatically.

Visual Representation:

new Cookie("star", "chocolate")
      β”‚
      β”œβ”€ Step 1: Create empty object β†’ {}
      β”‚
      β”œβ”€ Step 2: Link prototype β†’ {__proto__: Cookie.prototype}
      β”‚
      β”œβ”€ Step 3: Add properties β†’ {shape: "star", flavor: "chocolate"}
      β”‚
      └─ Step 4: Return object β†’ Cookie {shape: "star", flavor: "chocolate"}

Constructor Functions: The Blueprint Pattern πŸ“‹

A constructor function is just a regular function used with new. Naming convention: PascalCase (capitalize first letter).

// Constructor function
function User(name, email) {
  this.name = name;
  this.email = email;
  this.isActive = true; // Default value
}

// Create instances
const user1 = new User("Alice", "alice@example.com");
const user2 = new User("Bob", "bob@example.com");

console.log(user1.name);     // "Alice"
console.log(user2.name);     // "Bob"
console.log(user1.isActive); // true
console.log(user1 === user2); // false (different objects!)

Adding Methods to Instances

function User(name, email) {
  this.name = name;
  this.email = email;
  
  // BAD: Creates a new function for EACH instance
  this.greet = function() {
    console.log(`Hi, I'm ${this.name}`);
  };
}

const user1 = new User("Alice", "alice@example.com");
const user2 = new User("Bob", "bob@example.com");

console.log(user1.greet === user2.greet); // false 😱
// Each user has their OWN copy of greet function!
// Memory waste if you create 1000 users!

Solution: Use prototypes (next section).


Prototypes: Shared Methods πŸ”—

Every function in JavaScript has a .prototype property. Objects created with new inherit from this prototype.

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

// GOOD: Add methods to the prototype
User.prototype.greet = function() {
  console.log(`Hi, I'm ${this.name}`);
};

User.prototype.changeEmail = function(newEmail) {
  this.email = newEmail;
  console.log(`Email updated to ${this.email}`);
};

// Create instances
const user1 = new User("Alice", "alice@example.com");
const user2 = new User("Bob", "bob@example.com");

console.log(user1.greet === user2.greet); // true 
// Both users share the SAME function in memory!

user1.greet(); // "Hi, I'm Alice"
user2.greet(); // "Hi, I'm Bob"

Why this works:

user1
  β”œβ”€ name: "Alice"
  β”œβ”€ email: "alice@example.com"
  └─ __proto__ β†’ User.prototype
                   β”œβ”€ greet: function()
                   └─ changeEmail: function()

user2
  β”œβ”€ name: "Bob"
  β”œβ”€ email: "bob@example.com"
  └─ __proto__ β†’ User.prototype (SAME OBJECT!)

Real-World Examples 🌍

Example 1: Building a Car Factory

function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
  this.mileage = 0;
}

Car.prototype.drive = function(miles) {
  this.mileage += miles;
  console.log(`Drove \({miles} miles. Total: \){this.mileage}`);
};

Car.prototype.getInfo = function() {
  return `\({this.year} \){this.make} ${this.model}`;
};

// Create cars
const car1 = new Car("Toyota", "Camry", 2020);
const car2 = new Car("Honda", "Civic", 2021);

car1.drive(100);   // "Drove 100 miles. Total: 100"
car1.drive(50);    // "Drove 50 miles. Total: 150"

console.log(car1.getInfo()); // "2020 Toyota Camry"
console.log(car2.getInfo()); // "2021 Honda Civic"

Example 2: Todo List Items

function TodoItem(text) {
  this.text = text;
  this.completed = false;
  this.createdAt = new Date();
}

TodoItem.prototype.toggle = function() {
  this.completed = !this.completed;
  console.log(`"\({this.text}" is now \){this.completed ? 'completed' : 'incomplete'}`);
};

TodoItem.prototype.edit = function(newText) {
  this.text = newText;
  console.log(`Updated to: "${this.text}"`);
};

// Usage
const todo1 = new TodoItem("Learn JavaScript");
const todo2 = new TodoItem("Build a project");

todo1.toggle();      // "Learn JavaScript" is now completed
todo2.edit("Build an awesome project"); // Updated to: "Build an awesome project"

Example 3: Bank Account System

function BankAccount(owner, initialBalance) {
  this.owner = owner;
  this.balance = initialBalance;
  this.transactions = [];
}

BankAccount.prototype.deposit = function(amount) {
  this.balance += amount;
  this.transactions.push({ type: 'deposit', amount, date: new Date() });
  console.log(`Deposited $${amount}. New balance: $${this.balance}`);
};

BankAccount.prototype.withdraw = function(amount) {
  if (amount > this.balance) {
    console.log("Insufficient funds!");
    return;
  }
  
  this.balance -= amount;
  this.transactions.push({ type: 'withdraw', amount, date: new Date() });
  console.log(`Withdrew $${amount}. New balance: $${this.balance}`);
};

BankAccount.prototype.getStatement = function() {
  console.log(`Account Owner: ${this.owner}`);
  console.log(`Balance: $${this.balance}`);
  console.log("Recent transactions:");
  this.transactions.forEach(t => {
    console.log(`  ${t.type}: $${t.amount}`);
  });
};

// Usage
const account = new BankAccount("Alice", 1000);
account.deposit(500);   // Deposited \(500. New balance: \)1500
account.withdraw(200);  // Withdrew \(200. New balance: \)1300
account.getStatement();

Modern Alternative: ES6 Classes πŸ†•

In 2015, JavaScript introduced class syntax (syntactic sugar over constructor functions):

// Old way (Constructor Function)
function User(name, email) {
  this.name = name;
  this.email = email;
}

User.prototype.greet = function() {
  console.log(`Hi, I'm ${this.name}`);
};

// New way (ES6 Class)
class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }
  
  greet() {
    console.log(`Hi, I'm ${this.name}`);
  }
}

// Both create the same thing!
const user1 = new User("Alice", "alice@example.com");
user1.greet(); // "Hi, I'm Alice"

Under the hood, classes are just constructor functions! JavaScript converts class syntax to the function pattern.

Proof:

class User {
  constructor(name) {
    this.name = name;
  }
}

console.log(typeof User); // "function" 🀯

Common Gotchas & Solutions 🚨

Gotcha 1: Forgetting new

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

const user = User("Alice"); // Forgot 'new'
console.log(user); // undefined
console.log(window.name); // "Alice" (global pollution!)

Solution: Always use new, or use ES6 classes (they enforce it).


Gotcha 2: Returning Values

function User(name) {
  this.name = name;
  
  // Returning a primitive (ignored!)
  return 42;
}

const user1 = new User("Alice");
console.log(user1); // User { name: 'Alice' } (return ignored)

function User2(name) {
  this.name = name;
  
  // Returning an object (overrides!)
  return { customProp: "I'm different" };
}

const user2 = new User2("Bob");
console.log(user2); // { customProp: "I'm different" } 🀯

Rule:

  • Returning primitives (numbers, strings) β†’ ignored

  • Returning objects β†’ overrides the default return


Gotcha 3: Arrow Functions as Constructors

// Arrow functions CAN'T be constructors!
const User = (name) => {
  this.name = name;
};

const user = new User("Alice"); // TypeError: User is not a constructor

Why? Arrow functions don't have their own this binding. Use regular functions or classes.


Interview Questions πŸ’Ό

Q: What does the new keyword do?

Strong Answer: "The new keyword creates a new instance from a constructor function. It does four things: creates an empty object, sets the object's prototype to the constructor's prototype, executes the constructor with this bound to the new object, and returns the new object. This enables object-oriented programming in JavaScript."


Q: What's the difference between a regular function call and using new?

Strong Answer: "Without new, the function executes normally and this refers to the global object. With new, JavaScript creates a new object, binds this to that object, links its prototype, and returns the object. Forgetting new can cause bugs by polluting the global scope."


Q: What's the purpose of the prototype?

Strong Answer: "The prototype allows multiple instances to share methods, saving memory. Instead of each instance having its own copy of a method, they all reference the same function on the prototype. This implements inheritanceβ€”instances inherit properties and methods from their constructor's prototype."


Practice Challenge πŸ‹οΈ

Build a simple Product constructor with:

  • Properties: name, price, quantity

  • Methods: restock(amount), sell(amount), getTotal()

Solution:

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

Product.prototype.restock = function(amount) {
  this.quantity += amount;
  console.log(`Restocked \({amount} units. Total: \){this.quantity}`);
};

Product.prototype.sell = function(amount) {
  if (amount > this.quantity) {
    console.log("Not enough stock!");
    return;
  }
  this.quantity -= amount;
  console.log(`Sold \({amount} units. Remaining: \){this.quantity}`);
};

Product.prototype.getTotal = function() {
  return this.price * this.quantity;
};

// Usage
const laptop = new Product("Laptop", 999, 10);
laptop.sell(3);          // Sold 3 units. Remaining: 7
laptop.restock(5);       // Restocked 5 units. Total: 12
console.log(laptop.getTotal()); // 11988

By the time I finished making those hundred cookies, each one slightly different but all from the same cutter, I realized something profound:

Programming is about creating systems, not individual solutions.

The new keyword isn't just about making objectsβ€”it's about scaling patterns. One blueprint, infinite possibilities.

Just like my cookie cutter could make stars, hearts, or trees, a constructor function can create users, products, tasks, or anything you need. The pattern stays the same; the instances are unique.

Every time you use new, you're telling JavaScript: "Take this blueprint and build me something."

And JavaScript, like a loyal assistant, creates exactly what you asked for.


What's Next?

You now understand:

  • Constructor functions

  • The new keyword's four steps

  • Prototypes and inheritance

  • ES6 class syntax (modern alternative)

Next topics to explore:

  • class syntax in depth

  • extends and inheritance

  • super keyword

  • Private fields and methods