JavaScript's object prototype system is one of the most powerful yet frequently misunderstood features of the language. While developers coming from class-based languages like Java or C++ often struggle with JavaScript's prototypal inheritance, this mechanism is actually more flexible and powerful than traditional class-based approaches. Understanding how prototypes work under the hood is essential for writing efficient, maintainable JavaScript code and for truly mastering the language's object-oriented capabilities.
This guide explores the prototype system in depth, explaining how JavaScript objects inherit properties and methods through prototype chains, and why this knowledge matters for web performance and development.
What Are Object Prototypes?
Prototypes are the mechanism by which JavaScript objects inherit features from one another. Every JavaScript object has an internal link to another object called its prototype. The prototype chain continues until reaching an object with null as its prototype, which acts as the final link.
This system enables property and method sharing without duplication, making JavaScript memory-efficient and flexible.
According to MDN's guide on object prototypes, prototypes form the foundation of inheritance in JavaScript and allow objects to share behavior through the prototype chain.
The Prototype Chain Explained
When you try to access a property of an object, JavaScript follows a specific lookup process:
- First, it searches for the property in the object itself (the base object)
- If it doesn't find it there, it looks in the object's proto
- If it still doesn't find it, it continues up the chain, checking each proto until it either finds the property or reaches null
In our example with info3.fName1:
- JavaScript first looks in info3 - and it doesn't find fName1
- Then it checks info3.proto, which points to info2 - it doesn't find fName1 there, either
- Next it checks info2.proto, which points to info1 - and it finds fName1 here!
As documented in the MDN guide on inheritance and the prototype chain, this lookup process is fundamental to how JavaScript handles property access.
This is called the prototype chain, and this is how inheritance works in JavaScript.
1const info1 = {2 fName1: "Shejan",3 lName1: "Mahamud"4};5 6const info2 = {7 fName2: "Boltu",8 lName2: "Mia",9 __proto__: info110};11 12const info3 = {13 fName3: "Habu",14 lName3: "Mia",15 __proto__: info216};17 18console.log(info3.fName1); // "Shejan" - found through prototype chain19 20// Visual representation of the chain:21// info3 → info2 → info1 → Object.prototype → nullBuilt-in Prototype Methods
JavaScript's built-in objects all follow the prototype pattern:
- toString(), valueOf(), and hasOwnProperty() are inherited from Object.prototype
- Date objects inherit from Date.prototype, which inherits from Object.prototype
- Array methods like push(), pop(), and map() come from Array.prototype
- String methods for primitives work through temporary wrapper objects
When you create an object literal, its prototype automatically points to Object.prototype, giving access to all these shared methods.
1const myObject = {2 city: "Madrid",3 greet() {4 console.log(`Greetings from ${this.city}`);5 }6};7 8// These methods come from Object.prototype9myObject.toString(); // "[object Object]"10myObject.hasOwnProperty("city"); // true11myObject.valueOf(); // { city: "Madrid", greet: f }12 13// Check the prototype14Object.getPrototypeOf(myObject); // Object { }What is proto?
proto is a property that exists on every object instance (arrays, functions, objects - everything). It's an internal reference or pointer that indicates which prototype the object inherits from. By default, when you create an object, its proto points to Object.prototype.
According to freeCodeCamp's comprehensive guide, proto is the actual prototype link that connects objects in the inheritance chain.
1const myObject = {2 name: "Test"3};4 5// __proto__ is the actual prototype link on the object6console.log(myObject.__proto__ === Object.prototype); // true7 8// Recommended way to access prototype9console.log(Object.getPrototypeOf(myObject) === Object.prototype); // trueWhat is prototype?
When you create a function or class in JavaScript, the language automatically creates a blueprint object called prototype. This happens behind the scenes. The prototype property of a constructor function specifies the prototype that will be assigned to all objects created with that constructor.
As explained in the MDN documentation on inheritance, the prototype property is a key part of how constructor functions establish inheritance relationships.
1function Person(name) {2 this.name = name;3}4 5// JavaScript automatically creates Person.prototype6console.log(Person.prototype); // { constructor: Person }7 8// Add a method to the prototype9Person.prototype.sayHi = function() {10 console.log(`Hi, I'm ${this.name}`);11};12 13// The relationship:14// person.__proto__ === Person.prototype15// Person.prototype.constructor === PersonUsing Object.create()
Object.create() creates a new object and allows you to specify an object that will be used as the new object's prototype. This method provides a clean, direct way to establish prototype inheritance without constructor functions.
The MDN tutorial on object prototypes demonstrates Object.create() as the recommended approach for pure prototypal inheritance.
1const personPrototype = {2 greet() {3 console.log("Hello!");4 }5};6 7// Create object with specified prototype8const person = Object.create(personPrototype);9person.greet(); // Hello!10 11// Can still add own properties12person.name = "Alice";13person.age = 30;14 15console.log(person.name); // "Alice" (own property)Using Constructor Functions
When you call a function as a constructor with the new keyword, JavaScript automatically sets the prototype of the newly created object to the constructor function's prototype property.
Per the MDN inheritance guide, this is the traditional pattern for creating reusable objects in JavaScript.
1function Person(name) {2 this.name = name;3}4 5Person.prototype.greet = function() {6 console.log(`Hello, I'm ${this.name}`);7};8 9// Using new keyword10const person = new Person("Bob");11person.greet(); // Hello, I'm Bob12 13// Internal steps when using new:14// 1. Creates new empty object: {}15// 2. Sets __proto__: person.__proto__ = Person.prototype16// 3. Calls constructor: Person.call(person, "Bob")17// 4. Returns the objectUsing ES6 Classes
ES6 introduced class syntax, which looks different but works the same way under the hood. Classes are essentially syntactic sugar over JavaScript's prototype-based inheritance. Methods defined in a class are added to ClassName.prototype, and instances created with new have their proto set to the class's prototype.
As freeCodeCamp explains, understanding the relationship between classes and prototypes is essential for mastering JavaScript's object-oriented features.
1class Person {2 constructor(name) {3 this.name = name;4 }5 6 greet() {7 console.log(`Hello, I'm ${this.name}`);8 }9}10 11const person = new Person("Charlie");12person.greet(); // Hello, I'm Charlie13 14// What happens under the hood:15console.log(typeof Person); // "function"16console.log(Person.prototype); // { constructor: Person, greet: f }17console.log(person.__proto__ === Person.prototype); // true18 19// All these are equivalent:20// class syntax, constructor function, Object.create()Property Shadowing and Method Overriding
When a property exists on both an object and its prototype, the object's property takes precedence. This is called "shadowing" and applies to both data properties and methods.
According to the MDN documentation on prototype chains, shadowing is a key concept that affects how JavaScript resolves property access.
1const parent = {2 value: 2,3 method() {4 return this.value + 1;5 }6};7 8const child = {9 __proto__: parent,10 value: 4 // This shadows parent's value11};12 13console.log(parent.method()); // 3 (uses parent's value)14console.log(child.method()); // 5 (uses child's value)15 16// Override a method17child.method = function() {18 return `Child says: ${this.value}`;19};20 21console.log(child.method()); // "Child says: 4"Performance Best Practices
When working with prototypes, be mindful that each property lookup traverses the prototype chain. While modern JavaScript engines optimize this process significantly, extremely deep chains or frequent access to inherited properties can still impact performance.
Memory Efficiency: The true power of prototypes lies in their memory efficiency. When methods are defined on a prototype, they exist only once in memory and are shared across all instances. This is significantly more efficient than defining methods on each individual object instance.
As noted in the MDN performance guidelines, prototype-based inheritance provides significant memory savings compared to class-based duplication.
Understanding these optimization principles helps developers build high-performance JavaScript applications that scale efficiently.
1// BAD: Each instance has its own function2function BadPerson(name) {3 this.name = name;4 this.greet = function() {5 console.log(`Hi, I'm ${this.name}`);6 };7}8 9// GOOD: Method shared via prototype10function GoodPerson(name) {11 this.name = name;12}13 14GoodPerson.prototype.greet = function() {15 console.log(`Hi, I'm ${this.name}`);16};17 18// With 1000 instances:19// BAD: 1000 separate function objects20// GOOD: 1 shared function on the prototypeDebugging Prototype Issues
Understanding how to inspect and verify prototype relationships is crucial for debugging prototype-related issues.
// Check an object's prototype
const prototype = Object.getPrototypeOf(person);
// Verify inheritance relationship
console.log(Person.prototype.isPrototypeOf(person)); // true
// instanceof operator
console.log(person instanceof Person); // true
// Check if property is own or inherited
console.log(person.hasOwnProperty("name")); // true
console.log(person.hasOwnProperty("greet")); // false (it's on prototype)
Frequently Asked Questions
What is the difference between __proto__ and prototype?
__proto__ is a property on object instances that points to their prototype. prototype is a property on constructor functions that serves as a blueprint for instances created with that constructor.
How do I check if an object inherits from a specific prototype?
Use Object.getPrototypeOf(obj) === SpecificPrototype or obj instanceof ConstructorFunction. You can also check with SpecificPrototype.isPrototypeOf(obj).
Should I use classes or prototypes in modern JavaScript?
Use classes for most new code as they're more readable and familiar. Understanding prototypes is still important because classes use prototypes under the hood.
How do I create inheritance between two objects?
Use Object.create(parentObject) to create a new object with the specified prototype. You can also set __proto__ in object literals or use Object.setPrototypeOf().
Does using prototypes affect performance?
Modern JavaScript engines optimize prototype access well. The main performance benefit is memory efficiency--shared methods on prototypes use less memory than per-instance methods.
Conclusion
JavaScript's prototype system is a powerful and flexible mechanism for implementing inheritance and code reuse. While the distinction between proto and prototype initially confuses many developers, understanding how these pieces work together unlocks the full potential of JavaScript's object-oriented capabilities.
Key Takeaways:
- Every object has a proto property pointing to its prototype
- Constructor functions have a prototype property used by instances
- Property lookup traverses the prototype chain until found or null is reached
- ES6 classes are syntactic sugar over the prototype system
- Understanding prototypes enables better debugging and more efficient code
Whether you use constructor functions, Object.create(), or modern class syntax, prototypes are always working behind the scenes to make inheritance possible. Our web development team regularly applies these patterns to build maintainable, performant applications.