What is the Object Prototype in JavaScript?
Simply put, object prototypes are Javascript’s way of representing inheritance between objects. The prototype of an object (usually defined by the __proto__
property) is a reference to its parent object and it holds all the parent’s features that this object has inherited. This inheritance allows objects to access and utilize properties and methods defined in their prototype.
To demonstrate, let’s use the Object.create()
function to create an object that inherits from another:
const car = {
printMake () {
console.log(this.make);
}
}
// Create an object 'toyota' that inherits from the object 'car'
const toyota = Object.create(car);
// Let's set the 'make' property to 'Toyota'
toyota.make = 'Toyota';
toyota.printMake(); // Outputs: 'Toyota'
console.log(Object.getOwnPropertyNames(toyota)); // Outputs: [ 'make' ]
console.log(Object.getOwnPropertyNames(toyota.__proto__)); // Outputs: [ 'printMake' ]
In the example above, we used the car
object as our parent and created the toyota
object that inherits from it. After setting the make
property, we call printMake()
on toyota
in line 13. Notice that printMake()
was never defined on toyota
, as shown in line 15, it’s not one of its properties. However, we were able to call it because toyota
inherited it from car
.
Since the car
object is the parent, it’s referenced by toyota.__proto__
and printing its properties displays printMake
as shown in line 16.
How the prototype chain works
The prototype of an object is itself an object, meaning it also has to have a prototype of its own. This forms what’s called the prototype chain, which ends with an object whose prototype is null
.
When we try to access a property of an object, Javascript tries to find this property on the object itself. If it’s not there, it will check the properties of the object prototype. It will do this recursively down the prototype chain until the property is found or until it reaches the end of the chain.
So what if you create an object that doesn’t inherit from any other object? Will it have a null prototype? The answer is no. All objects in Javascript have a default prototype called Object.prototype
. Here’s an example:
const obj = {};
console.log(Object.getOwnPropertyNames(obj.__proto__)); // Outputs: ["constructor", "__defineGetter__", "__defineSetter__", "hasOwnProperty", "__lookupGetter__", "__lookupSetter__", "isPrototypeOf", "propertyIsEnumerable", "toString", "valueOf", "__proto__", "toLocaleString"]
We defined an empty object obj
, we then accessed the properties of its prototype which turned out to be:
[
"constructor",
"__defineGetter__",
"__defineSetter__",
"hasOwnProperty",
"__lookupGetter__",
"__lookupSetter__",
"isPrototypeOf",
"propertyIsEnumerable",
"toString",
"valueOf",
"__proto__",
"toLocaleString"
]
These are the properties of the Object.prototype
. And because of this, we can access any of these properties on obj
and it will return its function definition, as opposed to returning undefined
if we tried to access a property that doesn’t exist.
const obj = {};
console.log(Object.getOwnPropertyNames(obj.__proto__));
console.log(obj.toString) // Outputs: toString() { [native code] }
console.log(obj.valueOf) // Outputs: valueOf() { [native code] }
console.log(obj.propertyThatDoesntExist) // Outputs: undefined
Now, if we try to go one level deeper into the prototype chain and check the prototype of Object.prototype
, we’ll get back null. Meaning that we have reached the end of the prototype chain:
const obj = {};
console.log(obj.__proto__.__proto__); // Outputs: null
Object Prototype vs Class in Javascript
Classes were introduced in ES6 to make Javascript look more similar to other popular object-oriented programming languages like Java or PHP.
// Define a class
class Car {
constructor(make) { // constructor method
this.make = make; // property
}
getMake() { // method
return this.make;
}
}
// Create an object of the Car class
let myCar = new Car("Toyota");
// Use the getMake method to get the make of the car
console.log(myCar.getMake()); // Outputs: Toyota
In the code above, we defined a class Car
and we created myCar
which is an instance of the class. Now myCar
has the getMake()
property on it; or has it?
Let’s check the properties of myCar
:
console.log(Object.getOwnPropertyNames(myCar)); // Outputs: ['make']
myCar
doesn’t have the getMake()
method. Instead, the method exists in the prototype of myCar
:
console.log(Object.getOwnPropertyNames(myCar.__proto__)); // Outputs: ['constructor', 'getMake']
This is because Javascript classes are not technically equivalent to classes in other programming languages. In fact, classes are primarily syntactical sugar over the existing Javascript prototype-based inheritance.
So under the hood, Javascript created an object called Car
, and then created a new object called myCar
using Car
as its prototype. The code above is equivalent to the following code that uses the Object.create()
function to create an object and set its prototype.
const Car = {
getMake() {
return this.make;
}
}
const myCar = Object.create(Car);
myCar.make = 'Toyota';
console.log(myCar.getMake()); // Outputs: 'Toyota'
So if you’ve been asking yourself what’s the difference between object prototypes and classes in Javascript, there’s really no difference. You can use whatever syntax you like better. Just keep in mind that classes in Javascript are just a layer above prototypes and they behave in a similar fashion.