Question
How JavaScript Prototype Works: Constructors, Inheritance, and new
Question
How does JavaScript prototype-based programming work? In particular, what is the purpose of the .prototype property, and how does it relate to creating objects with new?
Why does this code not work as intended?
var obj = new Object();
obj.prototype.test = function () {
alert("Hello?");
};
var obj2 = new obj();
obj2.test();
Is JavaScript classless, with instances acting as clones of an original object? What is the correct way to define a shared method for objects created with a constructor function?
function MyObject() {}
MyObject.prototype.test = function () {
alert("OK");
};
var obj = new MyObject();
obj.test();
Short Answer
JavaScript objects can inherit properties from other objects through the prototype chain. You will learn the difference between a function’s .prototype property and an object’s actual prototype, what new does, and why methods placed on a constructor’s prototype are shared by every instance.
Concept
JavaScript uses prototype-based inheritance. Instead of copying all methods into each newly created object, an object can delegate property lookups to another object called its prototype.
A key distinction is important:
- A constructor function has a public
.prototypeproperty. - Every object has an internal prototype link, commonly inspected with
Object.getPrototypeOf(object).
When you use a constructable function with new, JavaScript creates a fresh object and sets that new object's internal prototype to the function's .prototype object.
function MyObject() {}
MyObject.prototype.test = function () {
console.log("OK");
};
const obj = new MyObject();
obj.test();
obj does not have its own test property. When JavaScript evaluates obj.test, it first checks obj. It does not find test there, so it follows obj's prototype link to MyObject.prototype, where it finds the method.
This matters because all instances share one method function rather than each instance receiving a separate copy.
The original code is different:
const obj = new Object();
obj.prototype = {};
obj is an ordinary object, not a constructor function. Adding a property named prototype merely adds a normal property. It does not turn obj into something that can be called with new, and it does not change the prototype of any other object.
Modern JavaScript also has class syntax. Classes are not exactly the same as older constructor functions in every detail, but class methods are still stored on a prototype and instances still use prototype lookup.
Mental Model
Think of an object as a worker and its prototype as a shared instruction manual.
When you ask a worker for an instruction such as test:
- The worker looks in its own pocket first (its own properties).
- If it is not there, the worker checks the shared manual (its prototype).
- If it is still not there, JavaScript checks that manual's prototype, continuing up the prototype chain.
- If no object has the property, the result is
undefined.
A constructor function's .prototype is the instruction manual that will be assigned to future objects created with new Constructor().
Adding a prototype label to an ordinary object is like putting an unused folder named “manual” in the worker's pocket. JavaScript does not treat that folder specially.
Syntax and Examples
A constructor function is a function intended to create objects with new. By convention, its name starts with an uppercase letter.
function User(name) {
this.name = name;
}
User.prototype.greet = function () {
console.log(`Hello, ${this.name}!`);
};
const ada = new User("Ada");
const lin = new User("Lin");
ada.greet(); // Hello, Ada!
lin.greet(); // Hello, Lin!
ada and lin each have their own name value. However, they share the same greet function:
console.log(ada.greet === lin.greet); // true
console.log(Object.getPrototypeOf(ada) === User.prototype); // true
console.log(ada.hasOwnProperty("greet")); // false
What new does
For this expression:
const ada = new User("Ada");
JavaScript broadly does the following:
- Creates a new empty object.
- Sets its internal prototype to
User.prototype. - Calls
Userwiththisset to the new object. - Returns that object, unless
Userexplicitly returns another non-primitive object.
You can also create an object with a chosen prototype directly:
const animalMethods = {
speak() {
console.log(`${this.name} makes a sound.`);
}
};
const dog = Object.create(animalMethods);
dog.name = "Milo";
dog.speak(); // Milo makes a sound.
Here, Object.create(animalMethods) sets animalMethods as dog's prototype.
Step by Step Execution
Consider this code:
function Counter(start) {
this.value = start;
}
Counter.prototype.increment = function () {
this.value += 1;
};
const counter = new Counter(5);
counter.increment();
console.log(counter.value);
Step by step:
- JavaScript evaluates
new Counter(5). - It creates a new empty object.
- It links that object's internal prototype to
Counter.prototype. - It runs
Counter(5)withthisset to the new object. this.value = startstores an own property on the new object, so it becomes{ value: 5 }conceptually.- The new object is assigned to
counter. - JavaScript evaluates
counter.increment. counterdoes not have its ownincrementproperty, so JavaScript checksCounter.prototype.- It finds
incrementand calls it withthisset tocounter. - The method changes
counter.valuefrom5to6. - The final
console.logprints6.
The method is inherited, but the state it changes belongs to the individual counter object.
Real World Use Cases
Prototype lookup is used constantly in JavaScript, including when you do not explicitly write .prototype.
-
Array methods: Arrays inherit methods such as
map,filter, andpushfromArray.prototype.const prices = [10, 20, 30]; const withTax = prices.map(price => price * 1.2); -
String methods: Strings can use methods such as
includesbecause JavaScript provides methods throughString.prototype."admin@example.com".includes("@"); // true -
Domain models: A server-side app might create
User,Order, orInvoiceobjects with shared behavior. -
Reusable browser components: Shared methods can validate form data, format values, or update component state.
-
Custom data structures: A
QueueorTimerconstructor can keep per-instance data while exposing shared methods on its prototype.
Real Codebase Usage
In modern codebases, developers often write class syntax, factory functions, or plain objects. Understanding prototypes is still essential because JavaScript's property lookup and many built-in APIs rely on them.
Class syntax uses prototypes
class User {
constructor(name) {
this.name = name;
}
greet() {
return `Hello, ${this.name}`;
}
}
const user = new User("Ada");
console.log(user.greet());
console.log(Object.getPrototypeOf(user) === User.prototype); // true
The greet method is on User.prototype, not copied onto every User instance.
Prefer own data and prototype methods
A common constructor pattern keeps instance-specific data on this and shared behavior on the prototype:
function Session(token) {
this.token = token;
}
Session.prototype.isAuthenticated = function () {
return this.token.length > 0;
};
Use guard clauses in methods
Methods commonly validate input before performing work:
function BankAccount(balance) {
this.balance = balance;
}
BankAccount.prototype.withdraw = function (amount) {
if (!Number.isFinite(amount) || amount <= 0) {
throw new Error("Amount must be a positive number.");
}
if (amount > this.balance) {
return false;
}
this.balance -= amount;
return true;
};
Avoid modifying built-in prototypes in application code
Changing Array.prototype, Object.prototype, or other built-in prototypes can affect unrelated code and third-party libraries. Prefer standalone utility functions or your own types.
Common Mistakes
Treating an ordinary object as a constructor
This is broken because obj is not callable or constructable:
const obj = new Object();
const obj2 = new obj(); // TypeError: obj is not a constructor
Use a function or class with new, or use Object.create when you already have a prototype object.
Assuming .prototype is every object's prototype
const person = { name: "Ada" };
person.prototype = { greet() {} };
This creates an ordinary own property called prototype. It does not set the prototype used for property lookup.
Inspect the actual prototype with:
Object.getPrototypeOf(person);
Replacing a constructor's prototype without restoring constructor
function User() {}
User.prototype = {
greet() {
console.log("Hello");
}
};
This works for inheritance, but User.prototype.constructor now points to Object through inheritance rather than directly to User. If that property matters to your code, restore it:
User.prototype = {
constructor: User,
greet() {
console.log("Hello");
}
};
Putting mutable values on the prototype unintentionally
function Playlist() {}
Playlist.prototype.songs = [];
const first = new Playlist();
const second = new Playlist();
first.songs.push("Song A");
console.log(second.songs); // ["Song A"] — shared array
Put per-instance arrays and objects in the constructor instead:
function Playlist() {
this.songs = [];
}
Losing this when extracting a method
const user = new User("Ada");
const greet = user.greet;
greet(); // `this` is not `user`
Call the method through the object (user.greet()), bind it (user.greet.bind(user)), or use an arrow wrapper where appropriate.
Changing a prototype after instances exist without understanding the effect
Existing instances generally see newly added methods because they reference the same prototype object:
User.prototype.logout = function () {};
But if you replace User.prototype entirely, old instances keep their link to the old prototype while new instances use the replacement.
Comparisons
| Concept | Meaning | Typical use |
|---|---|---|
Constructor.prototype | The object used as the prototype for instances created later with new Constructor() | Defining shared methods for constructor-created objects |
Object.getPrototypeOf(obj) | Returns the actual prototype object that obj delegates to | Inspecting an object's prototype chain |
obj.property | Looks for a property on obj, then through its prototypes | Normal property and method access |
Object.create(proto) | Creates an object whose internal prototype is proto | Direct prototype-based object creation |
class | Syntax for constructors, prototype methods, and inheritance | Modern application code |
| Factory function | A function that explicitly returns an object | Creating objects without new or prototype inheritance |
Constructor function vs class
function Car(make) {
this.make = make;
}
Car.prototype.drive = function () {
return `${this.make} is driving`;
};
class Car {
constructor(make) {
this.make = make;
}
drive() {
return `${this.make} is driving`;
}
}
Both approaches create instances that can find drive through Car.prototype. Classes are usually clearer for new code, while constructor functions are common in older JavaScript.
Prototype inheritance vs cloning
Instances are not normally full clones of a constructor or its prototype. Each instance is a separate object that delegates missing property lookups to its prototype. This is why shared methods can exist once and be available to many instances.
Cheat Sheet
- A prototype is an object another object can delegate property lookups to.
Object.getPrototypeOf(obj)reads an object's actual prototype.Constructor.prototypeis used bynew Constructor()to set the new object's prototype.- Put per-instance state in the constructor with
this. - Put shared methods on
Constructor.prototype.
function User(name) {
this.name = name; // unique per instance
}
User.prototype.greet = function () { // shared method
return `Hello, ${this.name}`;
};
const user = new User("Ada");
user.greet();
Useful checks:
Object.getPrototypeOf(user) === User.prototype; // true
user.hasOwnProperty("name"); // true
user.hasOwnProperty("greet"); // false
Important rules:
- An ordinary object's
obj.prototypeis just a normal property unless some code uses it. - Do not store mutable per-instance values, such as arrays, on a shared prototype.
- Adding a method to an existing prototype is visible to instances linked to that prototype.
- Replacing a constructor's
.prototypedoes not update existing instances. classmethods are prototype methods.
FAQ
What is the difference between .prototype and __proto__ in JavaScript?
A function's .prototype is the object that new uses for future instances. __proto__ is a legacy accessor for an individual object's internal prototype. Prefer Object.getPrototypeOf(obj) instead of __proto__.
Does every JavaScript object have a .prototype property?
No. Functions commonly have a .prototype property. Ordinary objects may have a property with that name, but it has no automatic prototype behavior. Every ordinary object can have an internal prototype link, though objects made with Object.create(null) have no prototype.
Does new copy methods from the prototype?
No. new links the new object to the constructor's prototype. Methods are found later through property lookup, so they are normally shared rather than copied.
Why are prototype methods memory-efficient?
One method function can be stored on the prototype and used by all instances. If the method were created inside the constructor, each instance would usually receive a separate function object.
Can I use prototypes without new?
Yes. Use Object.create(prototypeObject) to make an object that delegates to a specific prototype.
Are JavaScript classes prototype-based?
Yes. Class syntax provides a more structured way to define constructor behavior and prototype methods. Instances of a class still use a prototype chain.
Should I use constructor functions or classes?
For most new code that needs this style of object modeling, use class syntax for readability. Learn constructor functions and prototypes because they explain how the language works and appear in older code.
What happens if an object and its prototype have the same property name?
The object's own property wins. It shadows the prototype property until you delete or change the own property.
Mini Project
Description
Build a small TaskList type that stores tasks separately for each list while sharing task-management methods through a prototype. This mirrors a common application pattern: each user, project, or board has independent data but uses the same operations.
Goal
Create task lists that can add, complete, and summarize tasks without sharing task data between instances.
Requirements
Requirement 1
Keep learning
Related questions
Abort Ajax Requests with jQuery jqXHR.abort()
Learn how to cancel an in-progress jQuery Ajax request with jqXHR.abort(), handle abort status safely, and avoid stale UI updates.
Access the Correct this Inside a JavaScript Callback
Learn why JavaScript this changes in callbacks and how to preserve an object context using bind, arrow functions, and event handler patterns.
Add Key-Value Pairs to JavaScript Objects
Learn how to add key-value pairs to JavaScript objects with dot and bracket notation, dynamic keys, examples, and common mistakes.