Objects and Prototypes in JavaScript
The Story
DesiEats starts with one restaurant object. Soon Priya adds two more restaurants. All restaurants need a name, a city, and an open() action.
Ananya can copy the open function into every object. That works, but it creates repeated functions and makes updates harder. Rahul shows her another JavaScript idea: objects can look for missing properties in a shared object called a prototype.
The restaurant objects keep their own data. They share common behaviour through one prototype. It is like three restaurant branches using one shared operations handbook while keeping their own menus and names.
What You Will Learn
- How JavaScript objects store properties
- How property lookup moves through the prototype chain
- How
Object.createbuilds an object with a chosen prototype - How constructor functions and
.prototypework - The difference between own properties and inherited properties
- Why direct prototype mutation and
__proto__should be avoided
The Big Idea
An object is a collection of properties. A property can hold data or a function.
Every ordinary JavaScript object also has an internal link to another object called its prototype. When JavaScript cannot find a property directly on the object, it checks the prototype, then that prototype's prototype, and so on. This sequence is the prototype chain.
Think of asking a restaurant worker for a recipe. The branch's own recipe book is checked first. If the recipe is not there, the worker checks the shared company handbook. If it is still missing, the search continues to the head office handbook.
Objects Store Data and Behaviour
You can create an object with an object literal. A property is a named value, and a method is a function stored on an object.
What just happened?
nameandcityare data properties.openis a method.thisrefers to the object before the dot at call time:restaurant.- The object owns these properties directly.
An object literal is a good choice when you need one object and do not need a reusable object-creation pattern.
Property Lookup Uses the Prototype Chain
An object can read a property from its prototype even when it does not own that property.
What just happened?
Object.create(restaurantRules)creates an object whose prototype isrestaurantRules.nameis found directly onrestaurant.acceptsOrdersis not found directly, so JavaScript finds it onrestaurantRules.Object.hasOwnchecks only the object itself. It does not count inherited properties.- Inside the method,
thisis still the object used to call the method.
What Is the Prototype Chain?
The prototype chain is a linked lookup path. JavaScript follows it when reading a property.
For the previous example, the path is approximately:
restaurant
|
v
restaurantRules
|
v
Object.prototype
|
v
null
Object.prototype is a common ancestor for ordinary objects. It provides methods such as toString. The chain ends at null, meaning there is nowhere else to search.
You can inspect the prototype with Object.getPrototypeOf.
What just happened?
menu delegates missing property lookups to menuRules. menuRules is itself an ordinary object, so its prototype is Object.prototype.
The word delegation means allowing another object to answer a request on your behalf. Prototype inheritance is mainly delegation: the object does not copy the method; it looks for the method elsewhere.
Own Properties and Inherited Properties
An own property belongs directly to an object. An inherited property comes from somewhere on its prototype chain.
This difference matters when you loop over data or decide whether an object contains a value itself.
What just happened?
Object.hasOwn(branch, "name")is true becausenamebelongs tobranch.categoryis inherited, soObject.hasOwnreturns false.- The
inoperator checks the whole prototype chain, so it returns true. Object.keyslists enumerable own properties, not inherited ones.
Object.hasOwn(value, key) when you need to know whether the object itself owns a property. Do not use value.hasOwnProperty(key) blindly because an object can have its own property with that name or a null prototype.Creating Shared Behaviour with Constructor Functions
Before class syntax, JavaScript commonly used constructor functions. A constructor function is a normal function intended to create objects with new.
The function's .prototype object is used as the prototype of objects created with that constructor.
What just happened?
new Restaurant(...)creates a new empty object.- That object's prototype is set to
Restaurant.prototype. - The constructor runs with
thispointing to the new object. nameandcitybecome own properties.describeis found on the shared prototype.- Both objects use the same function instead of storing separate copies.
The .prototype property on a constructor function is different from the internal prototype link of each created object. The constructor's .prototype is the object used as the shared delegation target.
new and Constructor Functions
The new keyword performs important steps. It creates an object, connects it to the constructor's prototype, calls the constructor with that object as this, and returns the object unless the constructor explicitly returns another object.
A class uses this same prototype-based system under the hood. Class syntax is usually easier to read, but it does not replace prototypes with a different object model.
Changing a Property Can Shadow an Inherited One
If an object writes a property with the same name as an inherited property, the new own property takes priority. This is called shadowing.
The restaurant now has its own special property. The shared default is still unchanged. This is useful for defaults, but it can be confusing if code assumes it is changing the shared prototype.
Real-World Example: Restaurant Objects
Here is a small DesiEats model using a constructor function and a shared prototype. Each branch stores its own data, while all branches share behaviour.
The summary function is stored once on Branch.prototype. The rating and location remain different for each object. This is the practical value of prototypes: own data plus shared behaviour.
Classes Still Use Prototypes
The class version looks different, but its normal methods are still shared through the prototype.
This connects the two topics: classes are modern syntax, while prototypes are the mechanism used to share ordinary class methods.
Common Mistakes and Gotchas
1. Confusing .prototype with an object's prototype
function Menu() {}
const menu = new Menu();
// Wrong idea: these are the same property on the same value.
console.log(Menu.prototype === menu.prototype); // -> false
// Fixed: inspect the object's internal prototype.
console.log(Object.getPrototypeOf(menu) === Menu.prototype); // -> true
Menu.prototype belongs to the constructor function. Object.getPrototypeOf(menu) returns the prototype linked to the created object.
2. Defining methods inside the constructor unnecessarily
function Order(number) {
this.number = number;
// Wrong for shared behaviour: a new function is created per order.
this.summary = function () {
return `Order #${this.number}`;
};
}
Put shared methods on Order.prototype so every order can reuse one function.
3. Using for...in without checking ownership
const defaults = { category: "food" };
const item = Object.create(defaults);
item.name = "Dosa";
// Risky: inherited enumerable properties can appear.
for (const key in item) {
console.log(key);
}
// Fixed: check ownership before using the key.
for (const key in item) {
if (Object.hasOwn(item, key)) console.log(key);
}
Use Object.keys when you want own enumerable keys directly.
4. Mutating a shared prototype at runtime
Changing a prototype after many objects have been created can make behaviour difficult to trace. Define shared methods in one place before creating objects when possible.
5. Using __proto__ in new code
__proto__ is a legacy accessor. Prefer Object.create, Object.getPrototypeOf, and Object.setPrototypeOf when you truly need prototype operations. In most application code, create the right prototype once rather than changing it later.
Code Review Corner
Review this code before revealing the answers:
function Delivery(name) {
this.name = name;
this.describe = () => `${this.name} delivery`;
}
Delivery.prototype.cancel = () => `${this.name} cancelled`;
const first = new Delivery("Lunch");
const second = new Delivery("Dinner");
console.log(first.describe === second.describe);
console.log(first.cancel());
Reveal the review
There are two issues:
describeis created separately for every instance. Put it onDelivery.prototypeas a normal function to share it.- The arrow function assigned to
canceldoes not get a dynamicthis. Itsthisis captured from the surrounding scope, not fromfirst. Use a normal function:
Delivery.prototype.cancel = function () {
return `${this.name} cancelled`;
};
Then first.cancel() uses first as this.
Interview Questions
What is a prototype?
A prototype is an object that another object can use as a fallback during property lookup. It provides delegation and shared behaviour.
What is the prototype chain?
It is the sequence of prototype links JavaScript follows when a property is not found directly on an object. The chain eventually ends at null.
What is the output?
const parent = { role: "manager" };
const child = Object.create(parent);
child.name = "Ananya";
console.log(child.role);
console.log(Object.hasOwn(child, "role"));
Answer: manager, then false. The value is inherited, not owned by child.
Why are methods often placed on a prototype?
All instances can reuse one function instead of storing a separate function on every object. This reduces duplication and expresses shared behaviour.
How are classes related to prototypes?
A class is syntax built on JavaScript's prototype system. Normal class methods are placed on the class prototype and are shared by instances.
What does new do?
It creates an object, links it to the constructor's prototype, calls the constructor with that object as this, and returns the new object.
Practice Challenges
Easy: Shared restaurant method
Create two restaurant objects with a shared describe method using Object.create.
const restaurantRules = {
describe() {
// Return a sentence using this.name and this.city.
}
};
const restaurant = Object.create(restaurantRules);
restaurant.name = "Priya's Kitchen";
restaurant.city = "Lucknow";
console.log(restaurant.describe());
Hint: The method should be found on restaurantRules, not copied onto restaurant.
Medium: Constructor and prototype
Create a Product constructor with name and price data. Add a shared label method to Product.prototype.
function Product(name, price) {
// Store the product data.
}
// Add a shared label method.
const product = new Product("Masala Dosa", 120);
console.log(product.label()); // -> Masala Dosa: INR 120
Hint: Use a normal function on the prototype so this is the product.
Hard: Safe own-property copy
Write copyOwnProperties(source) to return a new object containing only the source object's own enumerable properties, not inherited ones.
const defaults = { cuisine: "Indian" };
const dish = Object.create(defaults);
dish.name = "Dosa";
dish.price = 120;
function copyOwnProperties(source) {
// Return a copy containing only own properties.
}
console.log(copyOwnProperties(dish));
// -> { name: 'Dosa', price: 120 }
Hint: Object.keys gives you the keys you need.
Quick Recap
- Objects store properties and methods.
- Every ordinary object has a prototype link.
- Missing properties are searched for along the prototype chain.
Object.createcreates an object with a chosen prototype.- Constructor functions use their
.prototypeobject for shared methods. - Own properties belong directly to an object; inherited properties do not.
- Classes provide modern syntax for the same prototype-based object model.
- Prefer standard prototype APIs over the legacy
__proto__accessor.
What's Next
Prototype lookup explains where object behaviour comes from. Next, we will look more closely at execution contexts, this, and how JavaScript creates and runs functions.