The basic Javascript interview question
“Tell me about some differences between Java and Javascript.”
In the room that is big enough for 4, maybe 5 people max, I could literally hear a pin drop. I’m fairly confident when it comes to job interviews due to having done the co-op program at the University of Waterloo. The stakes are high for this one, however; I really like this team and want to get an offer.
Okay, sounds like a simple question. I feel a little uneasy at this open-ended question, but I tell the interviewer what I knew - compiled vs scripted, the syntax, where they are widely used, etc.
“Okay. You know Java uses Class inheritance, right? What about Javascript?”
Turns out he wanted to hear more about the difference between Class inheritance and prototypical inheritance.
Uh oh. Didn’t do my homework here. Prototypical inheritance? I never even used those words before.
Luckily, he considered me a junior and didn’t expect me to know anything. He simply wanted to understand what I already knew.
He quickly went over the concepts, which didn’t really stay in my head. So I looked them up in more detail, and here I am writing this post.
Empty, but not really
Let’s declare an empty object a
.
const a = {}
Printing this out in the console should show an empty object.
But wait, there’s more…
Why do we have __proto__
? Where did it come from?
__proto__
is also known as dunder proto. “Dunder” comes from “double underscore”.
It is an object, and it has been automatically assigned to our object by the Javascript engine.
When you are working with Javascript, in almost all cases your Javascript engine will be assigning dunder prototypes to your object.
For our object a
, we can see that our dunder prototype comes from the global Object prototype.
Prototypes are linked to their instantiated constructor. Using object literals will link it to Object.prototype. Using array literal will link it to Array.prototype
.
const a = {} // __proto__ constructor: Object()
const b = [] // __proto__ constructor: Array()
Prototype method chain
Back to our empty object
const a = {}
This object is empty, but you will find that we can run some functions on it.
a.toString() // "[object Object]"
How? Why?
If you refer back to the dunder proto of a
, you will see that there exists a toString()
method.
When the Javascript engine saw that a
itself did not have a method called toString()
, it then checked through the prototype, found a method, and executed it.
That’s how Javascript’s prototype chain works. If the method doesn’t exist on the top-level chain, then it looks through its prototypes.
There is also no limit on how many nested prototypes there can be.
Here’s an example of nested prototypes.
const ben = {}
const jerry = Object.create(ben)
Printing out jerry
in the console will output the following:
The upper level is coming from our ben
object, and the second layer is coming from the global Object prototype.
We can also run .toString()
on jerry
, which should work since jerry
is linked to ben
, which is linked to the global Object.prototype
.
jerry.toString() // "[object Object]"
Nothing surprising so far, right?
Now, let’s assign a toString()
function to ben
.
ben.toString = function () { return "Vanilla" }
We will see that jerry
is now also updated with the new function.
jerry.toString() // "Vanilla"
That is because the Javascript engine was able to find the function toString()
within the first prototype layer. It stops going down the prototype chain, since the method has been found.
Proto here, proto there, proto everywhere
You can see dunder prototypes in other common data structures, like arrays, sets and maps.
Each object will have their respective prototypes and its functions, as documented on MDN.
Array
const a = []
Set
const a = new Set()
const a = new Map()
That’s all. Hope you learned something. Happy developing :)