The full video and many others like it are all available as part of our Frontend Masters subscription.

Kyle Simpson

Kyle Simpson is an Open Web Evangelist from Austin, TX, who's passionate about all things JavaScript. He's an author, workshop trainer, tech speaker, and OSS contributor/leader.

Kyle Simpson

You Don't Know JS

Prototypes in JavaScript can be linked and share a parent-child relationship similar to a subclass and superclass. This is beneficial when extending a prototype to add additional methods. However, there are issues with constructor references.

Get Unlimited Access Now

Kyle Simpson: Here's why things start to get complicated with respect to that dot constructor, and when we start layering things, trying to model things as classes. So I have a function Foo that's representing a class, and mostly it's just a class because I used the capital letter, F. But I start adding things like an identify to its prototype, and then I want to create a child class that I call Bar.

And theoretically that child class Bar is supposed to inherit from, or extend, the parent class foo. So how do I accomplish that? Well, line 12 shows you how you would most likely accomplish that because what you need is, you need for Bar's prototype to extend Foo's prototype. Or in this case, what you really need is for Bar's prototype to link to Foo's prototype.

How do we accomplish that? Well we could do, as you see there in the comments on line 11, we could do Bar.prototype = new Foo because that would create a brand new object that was linked to Foo prototype like we want. But that would have the unfortunate side effect of actually calling the Foo function, and adding properties like this.me to it, which we don't want.

So, we can't really call the constructor. The other way we do it is this utility called, Object.create. Okay? That was standardized as of ES5. There's a simple three line polyfill, which I'll show you later, that allows you to use it before ES 5. But Object.create essentially does the first two of the four steps that I taught you that the new keyword does.

What were those first two steps that I told you?
Speaker 1: Create a new object.
Kyle Simpson: Create a new object.
Speaker 1: Link to links.
Kyle Simpson: And link it. It does those two, but it does not do steps three and four because there is no constructor here for it to bind to this to, and there's no need for it to return it.

So it does the first two which is what we want, and it doesn't do the last two which is what we don't want. Does everybody follow that? Okay. So Object.create is a nice useful helper for us. It will create Bar.prototype which is linked to Foo.prototype. We can add things to Bar prototype like Bar.protype.speak.

And indeed, when we say b1 = new Bar, and we say b1.speak, guess what's going to happen? It's going to delegate from b1 up to Bar prototype, and it's going to find a speak function, and it's going to see a this.identify. What is the this keyword going to be? It's going to be b1, right?

So we're going to say, is there a b1.identify? No. So we're going to delegate from b1 up to Bar protoype. Is there a Bar prototype identify? So we're going to keep going and we're going to go up to Foo prototype. Is there a Foo prototype identify? So we're going to execute Foo protoype, and he has a this reference.

What is his this reference going to be?
Speaker 2: b1?
Kyle Simpson: Still b1. Super unicorn magic. So this keyword always keep pointing to the thing we want it to, even if we've delegated 15 levels up the prototype chain. So this is a really powerful and useful mechanism, but there is some really subtle problems when you start trying to think about the stuff as parent and child classes.

The first one is something that I noted here on line 13. And we'll be able to see this in a diagram in just a moment, but what would happen, do you think, if we called b1.constructor? What do you think would happen? What would it point to? Well, let me not ask what it would point to.

Let me ask what should it point to? What should b1.constructor be?
Kyle Simpson: We would want b1.constructor to be the Bar function, right? If .constructor means was constructed by, we would expect for b1.constructor to point to Bar. But let's analyze what it actually is. b1, does it have a constructor property on it?

Speaker 1: No.
Kyle Simpson: No? So let's go to Bar prototype. Does Bar prototype have a constructor property on it?
Kyle Simpson: Not anymore. The default one that was there on line 8, did have a constructor property. But we threw that guy away and created a new one so that we could link him to Foo prototype.

So the new Bar prototype that we created by Object.create doesn't have a constructor property on it. So guess what happens? We then delegate up to Foo prototype. Does Foo prototype have a constructor?
Speaker 1: Yes.
Kyle Simpson: And what does Foo prototype's constructor point to?
Speaker 1: Foo.
Kyle Simpson: Foo. So we get the really bizarre behavior that b1.constructor is Foo.

That's weird. b1.constructor ought to be bar, right? How could it possibly be Foo? Foo didn't construct him. The problem is that .constructor doesn't mean was constructed by, like you think it does. It's just an arbitrary property that happens to exist or doesn't exist. Now, there's one way of solving that, and that is on line 13 you can add a constructor to the Bar prototype and point it manually at Bar.

But you can't just do Bar.prototype.constructor equals bar, the way it would seem like, because now you've created an enumerable property, and that's going to break your for-in loops. So you actually have to use Object.defineProperty and create a non-enumerable property with a value. It sucks, right? This is the kind of crap that we have to deal with when we start trying to force the JavaScript mechanism to pretend like it is something that it is not.

Just one of the many things that are syntactic problems, and it's one of the many reasons why for two decades now, nobody writes this code anymore. They just use user libraries to fix all of these problems. My thesis to you is, what if there was a way to get rid of having to use all the libraries.

What if there was a way to do this stuff without all of this complexity? And that's what I'm driving towards, a different way of doing it. Okay? Questions so far.
Kyle Simpson: All right, there's the linkage, and I want you to pay attention to that linkage as we go throughout the next several slides.

b1 is prototype linked, bracket, bracket p linked, to Bar prototype, which is bracket, bracket p linked to Foo prototype. That's the linkage. Those are the only objects we actually care about.

Ready to take your code to the next level?

Intense courses with world-class teachers and unlimited access to our growing library of videos for the great price of $39 per month.

Get Unlimited Access Now