Everything is an Object…or not!

If it is one thing that grinds my gears, is running into people who mistake prototypal inheritance for classical inheritance. Even worse, is when they argue for classical inheritance patterns without any willingness to admit they lack some prototypal knowledge. It does not help that we now have the class keyword in JS. This is not about what is superior and/or inferior but rather a distinction of the two. I will also try to track down the earliest ancestor to all JS objects.

TL;DR:

In case you do not want to read the entire post, then I will summarize it as follows: Classical inheritance is like building houses using a blueprint, while prototypal inheritance is building an apartment building, focusing on what should be shared communally. They both have their caveats, like how do you improve upon the house/flat in each instance? Do you first edit the blueprint in classical inheritance? Do you edit the new flat on the fly in prototypal inheritance or do you just add it to the apartment building for all to share? There are no obvious answers to these questions but understanding this difference could help you structure things much more efficiently.

class vs Object.create vs new:

For the uninitiated, each of the keywords above do the exact same thing. class is a nicer way to writing constructor functions (i.e new) in ES2015. Object.create is an easier (cleaner?) way to creating objects introduced in ES5. For clarity’s sake, here is some code written using a constructor function and new:


//Constructor function using 'new'
var Animal = function(name, sound){
    this.name = name;
    this.sound = sound;
}

Animal.prototype.makeSound = function(){
    console.log(this.sound);
}

var cat = new Animal("Tiger", "Meow");

cat.makeSound() //"Meow"

This is a trivial example but I wanted to highlight the usage of prototype (it’s in the name!). makeSound function is specifically assigned to objects prototype. If we have a behaviour that is not unique to one object, we can delegate it to a single instance. Whenever we need it, we can quickly borrow the method/property. How does an object borrow the properties? By having a link to its previous prototype through a hidden __proto__ or sometimes referred to as [[Prototype]] property.


//From the constructor function example

cat.__proto__ === Animal.prototype //true

//THIS IS NOT A RECOMMENDED BEHAVIOUR. NEVER REFER TO AN OBJECT's __proto__ PROPERTY DIRECTLY!!!
//IT IS HIDDEN FOR A REASON

Ancestry.com for Objects:

This is where it gets interesting. If this prototype behaviour is common in every single object, there must be some object where everything points to, right? Existentialism aside, this is absolutely correct. All* objects keep inheriting properties through the hidden __proto__ all the way up. If the cat object does not have a makeSound property, it checks up on its __proto__ object and uses that method. If it is not there, it checks a level deeper (i.e: __proto__ of its own __proto__). You can make this trip yourself by referring to the __proto__‘s until you get undefined but it will not be clear where exactly this object resides. Instead, lets use deductive and inductive reasoning.

I will go with the assumption that everything in JS is an object. With the exception of primitives, we can check for the prototype chain using the instanceof operator. However, instanceof only checks whether a function is a constructor for the object in question. In essence, if all constructor functions are objects (see earlier assumption) – they must all inherit from the Function object and/or function (?). To check for this:


    Animal instanceof Function //true

OK, this is getting somewhere. From here onwards, it will get confusing but please bear with me. Function object is actually just a normal JS function that all functions inherit from. So, it must also have a prototype property, from which all functions inherit. It will also have the usual __proto__ hidden property because everything is an object (if the assumption still holds). Because functions are also objects, they can also be a part of the __proto__ chain. As strange as it sounds, a function could play both the parts of a prototype and that of a hidden __proto__. Which is exactly what is happening with Function.


Function.__proto__ === Function.prototype //true

From here onwards, we check out of functions and move on to objects (we’ve closed the loop, so to speak). Function.prototype, the key to all functions, has no prototype property (it’s set to undefined but that’s not strange – you can do that to any function). It only has a __proto__ property, meaning that we still have a chain to follow. It seems to point back to the Object function. This is the natively available function that constructs empty objects. Seeing that Object is actually a constructor function, it should also have a prototype property. This property will presumably act as part of the __proto__ chain for all instances of the Object function. Turns out, Object.prototype is actually just a plain object. Surprisingly it does not have a __proto__. This is a first for an object in JavaScript. Maybe, just maybe, we have found the single source of truth for all JS objects. There are a few checks to verify this.


Object.prototype instanceof Object //false. This means it's the only object not to do so
Object.prototype.isPrototypeOf(Function) //true
Object.prototype.isPrototypeOf(Object) //true
Object.prototype.isPrototypeOf({})  //true. Looks like Object literals inherit from it too!

So, at this point I think it is fair to say that all JS objects*, through the __proto__ chain, point to Object.prototype. Feel free to disprove this! Bonus image below for visual learners.


© Professional JavaScript for Web Developers, 3rd Edition, Nicholas C. Zakas, ISBN: 978-1-118-22219-5

* : Someone rightfully pointed out that Object.create(null) has no __proto__, essentially removing it from the prototype chain. This is the equivalent of creating an object and setting the __proto__ to null.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s