Core idea
A prototype is the object JavaScript uses as a fallback source during property lookup. If a property or method is not found on the current object, JavaScript walks upward through the prototype chain until it finds the property or reaches null.
Why interviewers ask this
Prototypes explain inheritance in JavaScript, how built-in methods like map() exist, why methods should be shared, and the difference between constructor prototype and an object's internal [[Prototype]].
Why prototype matters
Imagine a classroom with 200 students. If every student keeps their own separate copy of the same rule book, memory is wasted. A better approach is to keep one shared rule book and let everyone refer to it. That is the mental model of prototypes in JavaScript.
Instead of copying the same methods into every object, JavaScript lets objects share behavior through prototypes. This makes code more memory efficient and gives JavaScript its inheritance model.
What is prototype in JavaScript?
Every object in JavaScript has an internal hidden link called [[Prototype]]. That link points to another object, and JavaScript uses that linked object when it cannot find a property on the current object itself.
const user = {
name: "Prakash"
};
console.log(user.name); // "Prakash"
console.log(user.toString); // inherited from Object.prototypeThe object user does not directly define toString, but JavaScript still finds it because user is linked to Object.prototype.
Important
Prefer Object.getPrototypeOf(obj) when reading an object's prototype. The older __proto__ property is widely supported, but it is considered legacy for day-to-day use.
prototype vs [[Prototype]]
This is the most common source of confusion, so keep this distinction crystal clear:
[[Prototype]]belongs to objects. It is the actual internal link used for inheritance.prototypeis a property found on functions, especially constructor functions.- When you create an object with
new, the new object's[[Prototype]]is linked to the constructor'sprototypeobject.
function Person(name) {
this.name = name;
}
Person.prototype.sayHi = function () {
console.log("Hi, I am " + this.name);
};
const p1 = new Person("Aman");
console.log(Object.getPrototypeOf(p1) === Person.prototype); // true
console.log(p1.__proto__ === Person.prototype); // true (legacy style)
console.log(Person.prototype.constructor === Person); // trueSo the rule is simple: function objects have a prototype property, and normal objects have an internal [[Prototype]] link.
How the prototype chain works
When JavaScript evaluates obj.someProperty, it searches in this order:
- Check whether
somePropertyexists directly onobj. - If not found, go to
obj's[[Prototype]]. - Keep moving upward until the property is found or the chain ends at
null.
const animal = {
eats: true
};
const dog = {
barks: true
};
Object.setPrototypeOf(dog, animal);
console.log(dog.barks); // true -> found on dog
console.log(dog.eats); // true -> found on animal
console.log(dog.run); // undefined -> nowhere in chainIf a property exists on both the object and its prototype, the object's own property wins. This is called property shadowing.
const parent = {
role: "teacher"
};
const child = {
role: "student"
};
Object.setPrototypeOf(child, parent);
console.log(child.role); // "student"Constructor functions and shared methods
Before ES6 classes became common, constructor functions plus prototypes were the standard way to create multiple objects with shared methods.
function Student(name, score) {
this.name = name;
this.score = score;
}
Student.prototype.getResult = function () {
return this.score >= 40 ? "Pass" : "Fail";
};
const s1 = new Student("Riya", 82);
const s2 = new Student("Kunal", 31);
console.log(s1.getResult()); // "Pass"
console.log(s2.getResult()); // "Fail"
console.log(s1.getResult === s2.getResult); // trueThe biggest benefit is in the last line: both objects share the same method reference. If you define the method inside the constructor, a new function gets created for every instance.
Memory-friendly pattern
Put shared behavior on the prototype. Put per-object data like name, score, or id directly on the object.
Object.create() inheritance
Another clean way to set up prototype-based inheritance is Object.create(). It creates a new object and directly sets its prototype.
const userMethods = {
greet() {
return "Hello " + this.name;
}
};
const user1 = Object.create(userMethods);
user1.name = "Aarav";
const user2 = Object.create(userMethods);
user2.name = "Siya";
console.log(user1.greet()); // "Hello Aarav"
console.log(user2.greet()); // "Hello Siya"In this example, greet lives in one shared place, and both user1 and user2 inherit it through their prototype link.
Built-in prototypes like Array and String
Built-in types in JavaScript also rely on prototypes. That is why arrays can use map(), strings can use toUpperCase(), and functions can use call().
const arr = [1, 2, 3];
const text = "hello";
console.log(Object.getPrototypeOf(arr) === Array.prototype); // true
console.log(Object.getPrototypeOf(text) === String.prototype); // true
console.log(text.toUpperCase()); // "HELLO"Arrays directly inherit from Array.prototype, and string values behave as if JavaScript temporarily wraps them so methods from String.prototype can still be used.
console.log(arr.map((item) => item * 2)); // [2, 4, 6]
console.log([].hasOwnProperty("map")); // false
console.log(Array.prototype.hasOwnProperty("map")); // trueThis proves that map is not stored on each array instance. It is shared through Array.prototype.
Array.prototype to Object.prototype chain
This is the exact parent-child relationship most learners miss:
arr
-> Array.prototype
-> Object.prototype
-> nullSo if JavaScript does not find a property on the array instance, it checks Array.prototype. If it still does not find it there, it goes one level higher to Object.prototype.
const arr = [10, 20, 30];
console.log(arr.push); // found on Array.prototype
console.log(arr.hasOwnProperty); // found on Object.prototype
console.log(arr.nonExistingMethod); // not found -> undefined
console.log(Object.getPrototypeOf(arr) === Array.prototype); // true
console.log(Object.getPrototypeOf(Array.prototype) === Object.prototype); // true
console.log(Object.getPrototypeOf(Object.prototype)); // nullThat means arr.push() comes from Array.prototype, while arr.hasOwnProperty()comes from Object.prototype. The array is the child, Array.prototype is its parent, and Object.prototype is the parent of that parent.
Interview shortcut
For normal arrays, the lookup path is usually:arr -> Array.prototype -> Object.prototype -> null
String.prototype and primitive strings
Strings are slightly special because "hello" is a primitive, not a normal object literal. Still, JavaScript lets you use methods like slice(), includes(), and toUpperCase().
const text = "hello";
console.log(text.toUpperCase()); // "HELLO"
console.log(text.includes("ell")); // true
console.log(Object.getPrototypeOf(text) === String.prototype); // true
console.log(Object.getPrototypeOf(String.prototype) === Object.prototype); // trueThe practical lookup path is:
"hello"
-> String.prototype
-> Object.prototype
-> nullSo if a method is not found on String.prototype, JavaScript looks upward to Object.prototype. That is why even strings can access methods inherited from the broader object chain.
const text = "hello";
console.log(text.valueOf()); // from String.prototype
console.log(text.hasOwnProperty("length")); // available through object lookup
console.log(text.randomMethod); // undefinedThe same idea applies to other built-ins too:
- Functions:
fn -> Function.prototype -> Object.prototype -> null - Arrays:
arr -> Array.prototype -> Object.prototype -> null - Strings:
text -> String.prototype -> Object.prototype -> null
Best practices and common mistakes
- Use
Object.getPrototypeOf()instead of relying on__proto__in teaching and production code. - Avoid changing built-in prototypes like
Array.prototypeunless you have a very strong reason. - Put shared methods on prototypes, but keep instance-specific data on the object itself.
- Prefer
classsyntax when readability matters, but remember that classes still work on top of prototypes. - Do not confuse
prototypeon functions with an object's[[Prototype]].
// Not recommended
Array.prototype.last = function () {
return this[this.length - 1];
};
// Better: create your own helper function
function getLastItem(arr) {
return arr[arr.length - 1];
}Interview takeaways
- JavaScript inheritance is prototype-based, not class-based at its core.
- Every object has an internal
[[Prototype]]link to another object ornull. - Functions have a
prototypeproperty used when objects are created withnew. - Property lookup walks upward through the prototype chain.
- Built-in methods like
map,filter,bind, andtoUpperCaseexist because of built-in prototype objects.
One-line summary
A prototype is JavaScript's shared backup object for property lookup, and the prototype chain is the path JavaScript follows to find behavior an object does not define itself.