Prototypal inheritance, the way that objects inherit from other objects in javascript. If you have been programming in other languages, they usually have class inheritance.
Although js has a class keyword introduced in es6, it's just syntactical sugar. js doesn't really have classes.
Let's try to dig deeper into this,
And before I start discussing prototypal inheritance, I wanna clear some things out in js ...
In js, everything is objects, I mean almost everything, now js has 7 data types, they are
numbers,
booleans,
string,
null,
undefined,
objects,
symbol // introduced new in es6
And all datatypes except objects are primitive types.
Except null and undefined, all js primitive types have wrapper functions, so when you call 'string'.slice(0,8) in js, behind the scenes the code that gets executed :
String('string').slice(0,8)
Now what about functions and arrays, well let's try to run the following code in the chrome console
typeof [] // object
typeof {} // object
typeof function(){} //function
I'll discuss functions later but let's talk about arrays, how they are objects, well arrays are a special kind of object in js.
You can do indexing in arrays and grab intended values.
But from where do you get the push, pop, splice methods in arrays. Well as you know, behind the scenes, our array gets wrapped by the wrapper function Array which exposes us to all these methods...
Now let's talk about functions , how they are objects ...
// method 2
function sum(a,b){
return a+b;
}
//method 2
const sum2 = new Function('a' , 'b' ,'return a+b')
sum(3,2) //returns 5
sum2(3,2) //returns 5
You can also create functions with the second method in js,
Also in functions, you can access properties like name and others and do the things that you would do with objects
function subtract(a,b){
return a-b
}
subtract.name // returns subtract
Well in plain words, functions are special objects which are callable ...
Now we can really dive into prototypal inheritance ...
Well, inheritance is how objects inherit properties and methods from one another
Try to run the code in the browser
const obj = {
a: 'hello',
b: 'bye'
}
obj1.__proto__ // return Object()
The code should return The base object constructor, Why is that
Because in js, objects inherit from the Base Object...
That's why you can use methods in objects like hasOwnProperty,
isPrototypeOf etc ... We didn't define these methods on our objects ... they got inherited ...
The _proto_ in js returns the object from which the object is inheriting
But what about arrays and functions,
//array
const arr = [1,2,3]
//function
function func(){
return 0;
}
console.log(func.__proto__)//returns Function()
console.log(arr.__proto__)//returns Array()
well in the case of functions and arrays they inherit from the base Function object and the base Array object respectively,
That's why we can assess methods like map, reduce, filter,foreach, etc. on arrays...Cause they got inherited...
But The base Function and Array Object, do they inherit as well
Let's find out
//array
const arr = [1,2,3]
//function
function func(){
return 0;
}
console.log(Array.__proto__)//returns base Object
console.log(Function.__proto__)//returns base Object
console.log(func.__proto__.__proto__)//returns base Object
console.log(arr.__proto__.__proto__)//returns base Object
Well, they inherit from The base Object, so All of them at the end inherit from the base Object
Now let's look at the code below
//array
const arr = [1,2,3]
console.log(arr.__proto__)//return Base Array object
console.log(Array.prototype)//return Base Array object
They both return The base Array object...
In fact, the _proto_ actually returns the prototype of the inherited object,
In this case, our arr inherited from Array, and the arr._proto_
actually pointed towards the prototype property of Array or Array.prototype...
So when you create objects, a _proto_ property is also linked with it which points up the prototype chain...
If you are wondering what's the prototype chain, well it should be easy to understand now,
const arr = [1,2,3]
arr.map(el=> el*2)
In the above code snippet, we are mapping through an array and multiplying 2 with every element...
Well, if we console.log arr we should see something like this
We don't have the map function here, so when javascript fails to fine the map object in the array, javascript goes up the prototype chain, which means it goes to the prototype of Array object, there it finds the map object and thus we can use it...
But what happens if we use a method that is not defined above the prototype chain as well
const arr = [1,2,3]
arr.lol(el=> el*2)
well arr doesn't have the lol method, so js goes up the prototype chain, and goes to the prototype of the base Array object, there also js doesn't find the map method, so js goes up the prototype chain again, and reaches the prototype of the base Object, there also js doesn't find the map method, so js goes up the prototype chain again, well what do you expect that js found,
const arr = [1,2,3]
console.log(arr.__proto__.__proto__.__proto__)//returns null
Js found's null, here we go the end of prototype chain, which is the base Object, as everything in js inherits from it if we go up the prototype chain of base Object, javascript returns null, cause null in js means the thing doesn't exist, this is often referenced null pointer, in this case, js doesn't go up the prototype chain and returns an error or undefined...
So in one of our code snippets above, when we try to access arr.lol() it's gonna return error...
Now as we understand prototypal inheritance, you may say, how all of this is useful to us...
const human = {
walk(){
console.log('walking....')
} ,
sleep(){
console.log('sleeping....')
} ,
describe(name){
console.log(`my name is ${name}`)
}
}
const programmer = {
code(){
console.log('coding ...')
}
In the above code, we have a human and a programmer object, suppose we want our programmer to walk, sleep and describe himself, cause technically, programmers are human
One way could be to just copy the sleep, walk and describe method and paste in on programmer object, but as programmers, we should never repeat ourselves, This is where prototypal inheritance comes into play,
We can make programmer object inherit from human
const human = {
walk(){
console.log('walking....')
} ,
sleep(){
console.log('sleeping....')
} ,
describe(name){
console.log(`my name is ${name}`)
}
}
const programmer = {
code(){
console.log('coding ...')
}
}
programmer.__proto__ = human
for(const i in programmer){
console.log(i)
}
you should get
code
walk
sleep
describe
So our programmer object is inheriting walk , sleep , describe from human object ...
Another thing we could do is,
for(const i in programmer){
if (programmer.hasOwnProperty(i)){
console.log(i)
}
}
you should get
code
This only logs code() method , cause the code method is owned by programmer object , programmer object didn't inherit code method from any other object , so when checking programmer.hasOwnProperty(i), that returned true...
Let's verify that the programmer object is inheriting from human object
human.isPrototypeOf(programmer) //returns true
Here we use the isPrototypeOf method available on objects to check if human is a prototype of programmer, or does programmer inherit from human
programmer.isPrototypeOf(human) //returns false
Here, we are checking the other way around
We are checking if the programmer is a prototype of human or does human inherit from programmer,
well human does not inherit from programmer surely, programmer inherits from human, and thus it returns false
One last thing about prototypal inheritance which is going to be super confusing and shocking and it's going to blow your mind,
Only functions have the prototype property 💥🤯🤯💥
let's verify that , the base Object,Array , Function are actually functions
typeof Array //Function
typeof Function //Function
typeof Object //Function
So they have the prototype property, and our normal arrays, objects don't have the prototype property, I know, super confusing ...
So to review everything in js is an object, everything inherits the base Object in js through prototype chain, we can go up the prototype chain looking for properties on the prototype property, the _proto_ links higher up to the next prototype change, _proto_ links the prototype property and only functions have prototype property...
And another thing is that you shouldn't really use the _proto_ for inheritance, it's bad for performance and there are many more efficient ways to inherit in js, one way is Object.create()
const human = {
walk(){
console.log('walking....')
} ,
sleep(){
console.log('sleeping....')
} ,
describe(name){
console.log(`my name is ${name}`)
}
}
const programmer = Object.create(human) // does the same thing as programmer.__proto__
programmer.code = () => {
console.log('coding ...')
}
for(const i in programmer){
console.log(i)
}
I hope you finally understand what exactly is prototypal inheritance...
If you found this helpful or useful, please share a 💓, 🦄, or 🔖. Thanks!
Top comments (2)
Nice article.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.