From Prototype Chain to Classes: Understanding JavaScript’s Real OOP Model
JavaScript Prototypes Demystified: How Objects, Constructor Functions, and Classes Really Work

What You Will Learn From This Blog
By the end of this article, readers will:
Understand how JavaScript methods exist even when we never define them on variables
Learn how prototype chaining works internally
Understand the difference between
prototypeand__proto__Learn how
Object.create()enables inheritanceUnderstand constructor function inheritance
See how modern ES6 classes are built on top of prototypes
Introduction
JavaScript’s object system often confuses developers, especially when they encounter terms like prototype, constructor functions, and classes.
Many developers use classes in JavaScript without understanding that under the hood, everything is still based on prototypes.
In this blog, we will explore:
How prototype chains work
How constructor functions create objects
How inheritance works using prototypes
How ES6 classes are simply a cleaner syntax built on top of the prototype system
Once you understand these concepts, JavaScript’s object model becomes much easier to reason about.
Understanding the Prototype Chain
In JavaScript, almost everything is an object, and objects inherit properties and methods from other objects through the prototype chain.
Consider this example:
let str = "hello";
str.toUpperCase();
Here’s the interesting part:
We never defined toUpperCase() on the variable str.
So how does this method work?
Internally, JavaScript looks for the method in the String object's prototype.
Primitive strings do not have methods themselves. When a method is accessed, JavaScript temporarily wraps the primitive value in a String object, allowing it to access methods from String.prototype.
Conceptually, the engine performs something like this:
let temp = new String("hello");
temp.__proto__ = String.prototype;
return temp.toUpperCase();
This is how JavaScript allows primitive values to use object methods.
The Prototype Chain Explained
When JavaScript tries to access a property, it follows this order:
object → object.__proto__ → parent prototype → Object.prototype → null
For a string example, the chain looks like this:
str
↓
String.prototype
↓
Object.prototype
↓
null
Object.prototype It is the top-level prototype from which almost all JavaScript objects inherit.
When JavaScript reaches null, the lookup stops.
prototype vs proto
Developers often confuse these two concepts.
| Concept | Description |
|---|---|
prototype |
A property of constructor functions used when creating objects |
__proto__ |
A property of all objects that points to their prototype |
Example:
function Person() {}
const p = new Person();
console.log(Person.prototype);
console.log(p.__proto__);
Both refer to the same prototype object.
Object Inheritance with Object.create()
Suppose we have an object:
let person = {
name: "viswa",
age: 24
};
Now we want another object to inherit these properties.
We can use:
let developer = Object.create(person);
This does two things:
Creates a new empty object
Sets its internal prototype to
person
Now:
developer.name
JavaScript will search:
developer → person → Object.prototype
Even though developer doesn't contain nameIt finds it in the prototype.
You can also add new properties:
developer.language = "JavaScript";
Constructor Functions and Object Creation
Before ES6 classes existed, JavaScript used constructor functions to create objects.
Example:
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.greet = function() {
console.log("Hi my name is", this.name);
};
Creating an instance:
const p1 = new Person("Vish", 25);
Internally, JavaScript does something similar to:
const obj = {};
obj.__proto__ = Person.prototype;
Person.call(obj, "Vish", 25);
return obj;
This is how constructor functions create instances.
Inheritance Using Constructor Functions
Now, let's create a Developer constructor that inherits from Person.
function Developer(name, age, language) {
Person.call(this, name, age);
this.language = language;
}
Next, we connect the prototypes:
Developer.prototype = Object.create(Person.prototype);
Developer.prototype.constructor = Developer;
Now instances of Developer can access methods from Person.prototype.
Example:
const d1 = new Developer("Viswa", 25, "JavaScript");
d1.greet();
ES6 Classes (Just Syntactic Sugar)
Modern JavaScript introduced classes, which provide cleaner syntax.
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log("Hi my name is", this.name);
}
}
Inheritance using classes:
class Developer extends Person {
constructor(name, age, language) {
super(name, age);
this.language = language;
}
}
Even though this looks different, JavaScript still uses prototypes internally.
Classes simply make the syntax easier to read and manage.
Conclusion
JavaScript’s object model can seem confusing at first, especially when dealing with prototypes and inheritance.
However, once you understand the prototype chain, everything starts to make sense.
The key takeaways are:
JavaScript objects inherit properties through the prototype chain
Constructor functions create instances using the
newkeywordObject.create()allows objects to inherit directly from other objectsES6 classes are built on top of prototypes, not separate from them
Understanding these concepts gives you a deeper knowledge of how JavaScript actually works under the hood.
And once you master prototypes, debugging complex JavaScript behavior becomes significantly easier.

