Prototypal Inheritance
OOP in JavaScript is built on prototypal inheritance. Objects can be chained so that a property not found in one object is automatically searched for in another. The link here is a special internal property, [[Prototype]]
, which is displayed as __proto__
in the browser console.
Object prototype
The Object.create(obj)
method creates and returns a new object, linking it to the obj
object.
const animal = {
legs: 4,
};
const dog = Object.create(animal);
dog.name = "Mango";
console.log(dog); // { name: 'Mango', __proto__: animal }
console.log(animal.isPrototypeOf(dog)); // true
The object referenced in __proto__
is called a prototype. In our example, the animal
object is the dog
object’s prototype. The isPrototypeOf()
method checks if the animal
object is dog
’s prototype and returns true
or false
.
console.log(dog.hasOwnProperty("name")); // true
console.log(dog.name); // 'Mango'
console.log(dog.hasOwnProperty("legs")); // false
console.log(dog.legs); // 4
The dog.name
reference is obvious: it returns the dog
object’s own property, name
. When accessing dog.legs
, the interpreter looks for the legs
property in the dog
object, cannot find it and continues searching in the object by reference from dog.__ proto__
, that is, in the animal
object, its prototype.
The prototype is a backup storage of object properties and methods, which is automatically enabled when searching for them. An object that acts as a prototype can also have its own prototype, etc.
The property is searched for until the first match is found. The interpreter searches for a property by name in the object; if not found, it refers to the __proto__
property, that is, it follows the link to the prototype object, and then to the prototype of this prototype. If the interpreter gets to the end of the chain and does not find a property with the given name, it will return undefined
.
In the specification, the __proto__
property is denoted as [[Prototype]]
. The double square brackets are important, as they indicate that this is an internal property.
hasOwnProperty()
method
Now that you have learned about how to find properties of an object, you should understand why the for...in
loop does not distinguish between properties of an object and its prototype.
const animal = { eats: true };
const dog = Object.create(animal);
dog.barks = true;
for (const key in dog) {
console.log(key); // barks, eats
}
This is why you use the obj.hasOwnProperty(prop)
method, which returns true
if the prop
property belongs to the obj
object, and not to its prototype; otherwise it returns false
.
const animal = {
eats: true,
};
const dog = Object.create(animal);
dog.barks = true;
for (const key in dog) {
if (!dog.hasOwnProperty(key)) continue;
console.log(key); // barks
}
The Object.keys(obj)
method will return an array of own keys of the obj
object, which is why in practice it is used instead of for...in
.
const animal = {
eats: true,
};
const dog = Object.create(animal);
dog.barks = true;
const dogKeys = Object.keys(dog);
console.log(dogKeys); // ['barks']