JavaScript Performance

Scoping and Prototypes

Steve Kinney

Steve Kinney

JavaScript Performance

Check out a free preview of the full JavaScript Performance course

The "Scoping and Prototypes" Lesson is part of the full, JavaScript Performance course featured in this preview video. Here's what you'd learn in this lesson:

Scoping can have dramatic implications on the performance of our code. Steve investigates with the curious case of a constructor class inside of a function. He compares the hidden classes of objects that were created by what looks like the same constructor.


Transcript from the "Scoping and Prototypes" Lesson

>> Steve: So let's do one other thing to play around with. It'll be like our kind of second to last science experiment just in the land of the JavaScript browser engine. Let's try something else.
>> Steve: It turns out that how you scope your JavaScript can have some important implications into how things run.

Let’s go back to our good friend benchmark. I’m gonna make a little test function just cuz there’s a lot of stuff happening. We’ll have our test function, all right, neat. We’ll say like it'll take a point and it'll add its x and its y, I don't know. point.x + point.y, and we'll go ahead, make that x and y, so this.x = x.

This.y = y, very cool. And then in this function, we'll make a new point.
>> Steve: And we'll add the x and y at the point. All right, make an object, it's got an x and y, create one of them. We'll add them together.
>> Steve: So we'll go in here.

And we'll just say, call that test function every time. I don't wanna measure me making the whole thing. I'm just gonna run it in the exercise part. And not to ruin the surprise, but I'm gonna turn this number down a little bit.
>> Steve: 800 milliseconds, that seems like quite a bit considering I had that much larger iteration for just adding 2 things, and it was 47.

I think this would go for about eight seconds, if I let it really go for it. That seems unacceptable. And it’s like weird to me, right? How is this that much different? I don’t know. I’ve measured. I know that it’s bad. I’m just gonna move some code around.

Don’t mind me. Let's go ahead and put this up here, right? I don't need to define the class in every iteration.
>> Steve: Nine milliseconds, [LAUGH] that's a wee bit faster. Okay, now one would argue, well, you are creating a class in every run. I don't think that's what got me from 9 milliseconds to 800, right?

It seems like there might be something else going on here. And so I have some suspicions. So what I'm gonna do is I'm gonna just try one more thing. Remember when I was comparing all those types? I wanna try one other thing. I'm gonna go ahead and I'm gonna grab this point and bring it back to the classes one.

I'm gonna do some science here. I had it already, that's great. So in the other case, I was making one of these inside the test function, and the other one I had it up a scope. I had it the same point class, I wasn't rewriting it all the time.

So I've run this code, I know that these are the same type. What if I had a,
>> Steve: We'll return a new point.
>> Steve: And we'll say make a point.
>> Steve: And we'll say make a point.
>> Steve: And we'll see if they have the same map.
>> Steve: They don't, but they were made from the same infrastructure that worked last time, right?

We saw it. I'm not losing it, right? We saw that when I used the point class last time that they were the same. And now I'm doing it inside this function and they're different. Also, it's way slower. Like 9 milliseconds versus 800. Any guesses? When we make the point inside of a function, it's not parsing, theoretically.

We are doing it every time, that is true. But it's not the parsing making the big difference. We're making this class as a different prototype chain every time, right, which means that each one of these points that come out of here points to a different prototype object. We're making a fresh point function every time, so on and so forth.

And we can see that this little, tiny difference in where I've put that code clearly had dramatic effects on how slow my code was. 9 milliseconds, 800 milliseconds, right? Now, does this mean that during lunch today we go through our code bases and we look for every time I've got a class inside another function?

No, but it does notice you open up your web application while it's running in production mode. You go later and see, you run the performance thing, you're like, wow, that's taking a really long time, right? Then, you begin to spelunk in there. And at least you can have some hypotheses about how this stuff actually works, so that you're not just throwing stuff at the wall, right?

I know that if I'm making stuff inside of a function it can have a different chain, I know if they went through different paths to get to the same object, that can be the problem, right? Do not speculatively go through your code base and do this everywhere. But I'm like, this is taking too long to load.

Or like I see the little red triangle there. Or I see that I'm pegging the CPU or something along those lines. Now, yes, we have some hypotheses on what's happening here, right? We haven't even touched the browser land yet, which can have a whole bunch of other implications.

But we do know that not all JavaScript is created equal. And we know that how our code is structured can have some pretty dramatic impacts on that. We're gonna look at one more now, one more thing that turbofan does to you. And the good news is it's like, hey, it does this thing for me and it just works out most of the time and everything is great, right?

There's no action item for you, which is big relief after, I have to worry about scoping and how my objects are made and all that kinda stuff. So this next one'll be a little bit more fun. Fun is a strong word when we're talking about JavaScript compilers. So, yeah, but the takeaways from the previous section, turbofan is able to optimize your code in substantial ways if you pass it consistent values.

So what does that really mean for us? If you can have a function that creates the object, rather than you doing it by hand every time, if you're always making a object literal, yes, you could type the curly braces and the properties, right? But if you know if you're using a lot, it might make sense to make a function that gives you a consistent object that was created the same way every time.

And that way, you don't have to think about this. Every moment you're thinking about all the stuff that we just kinda talked about, it's like you probably have some changes you need to make in the architecture of your code. And try not to modify them after the fact.

Maybe it makes sense to make a new object that gets created the same way. Cuz that will have the same map as all the other objects made that way. But probably the real easy way to do this is we all get really happy about TypeScript or Flow and like, yeah, I'm gonna do it.

And then I have to make a type and I don't understand TypeScript or Flow and I say to myself, you know what? I'm just writing this in JavaScript. It's not worth it for me, [LAUGH] right? There are certain advantages that these systems above avoiding runtime errors and stuff like that, there are things to do that the optimizer can get ready for if you follow this code of ethics by having the same types all the time, right?

So most of the answers here are either measure and see what's happening and, hopefully, you're a little bit better at making hypotheses now. Or, b, the point I'm gonna end with at the end of the day is, be great if we could just have tools do this stuff for us, right?

That is ultimately the right answer.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now