JavaScript's new keyword, under the hood 🐬

Or, how classes are implemented in terms of prototypes

JavaScript is a wonderful general programming language. It provides a lot of features that allows you to write imperative, functional, and object-oriented code. However, JS’s object oriented features are implemented in a fairly different way to other languages such as Java and C++, which has led to confusion over the years. This confusion has also created many bugs and misunderstanding around JS’s legitimacy as a language.

A key point to start from, is that object-oriented does not mean “classes and inheritance” — they’re just a common way to achieve it. As such, Javascript does not have classes in the traditional sense, no matter what ES6 looks like. Javascript has prototypes, and “class-like” prototype syntax.

This is not a beginner’s guide to using new — it is a deeper dive into lower level building blocks that make JavaScript what it is.

If you are coming from C++, Java, C# or any other language that has a new keyword, be aware that each language handles new in its own unique way — and prior knowledge of another language may actually confuse what’s happening in JavaScript!

.

At a glance 👓

  • If you want object oriented code, use the Class keyword
  • Javascript’s Class syntax is actually hiding prototypical inheritance
  • Functions are secretly objects
  • Prototypes save us memory
  • The new keyword makes a function call act in a constructor-like manner

Follow the code

Follow along at https://codesandbox.io/s/javascript-new-keyword-animated-bouncing-balls-nu43i

.

Setting the scene 💺

Our boss at the local niche hardware store, “Shovels and Chairs” has asked us to create a web-based bouncing ball simulation. It’s for an advertising campaign, and we need to impress potential customers. As such, we’ve got a requirement that we can display 1,000 balls bouncing on a screen at the same time — so we’re going to need to be somewhat efficient.

Object: ball#0. x: 25, y: 5, speed: 0, radius: 10, color: 'red'

However, there’s only one ball here, and we want to make many.

Let’s extract a makeBall construction function that will give us a new Ball object. We’ll also roll in the update function so we can call ball.update(delta) rather than updateBall(ball, delta).

(This means that in good OOP encapsulated style, we’re only fiddling with ball’s members in private, not public.)

Inside makeBall , we create a ball object, and after creation add a new update function to it.

Let's take a look.

Three bouncing red balls.
The balls! They bounce!
.

Functions are actually objects 🐬🐳

Just like how all dolphins are actually whales, it’s worth noting at this point that all functions are ac tually objects. This has many implications, most of which we’re not interested in today — but it does mean that every time you write function or () => { }, you’re creating a new object with the function code in it. This is different to C++ and Java, where the code for each function only exists once in the compiled assembly.

That means, if we created 3 balls, our memory layout would look like this:

Note how the code for the “update” function is stored in three locations, once for each ball object.

That’s a lot of wasted memory to store the update functions, which are identical across them all. Our boss certainly isn’t going to splash out for more RAM to display this, and our customers are getting antsy to see what technical marvel they’ve been promised.

Is there a way we can still couple the data and code closely, but only store the update code in one location?

.

Let’s bring in this and new

We’re going to rewrite the code to make use of this and new — note that we don’t explicitly declare this anywhere. The new keyword will provide it for us.

Essentially, we’re replacing any reference to ball with the word this.

In this new snippet, we’ve renamed the makeBall function to Ball, replaced references to ball with this inside it, and on line 24, added the new keyword before calling the function.

We haven’t actually declared the this variable at this point, which means calling this function will actually throw an error now (if we didn’t return on line 4!) We’ll see where this variable gets set later.

Right now, there is no obvious advantage to having done this. It’s fairly equivalent to the previous code. So why would we have made this change? What new power has it given us?

.

Enter the prototype 🤸‍♂️

In C++, Java, C#, etc, all functions exist just the once in the compiled code — what if we attempted to do a similar thing in JS?

In JS, all objects have a hidden value internally called prototype which points to another object. Yes, all objects. Even an empty object like {}.

You can see this is in some browsers like Chrome by typing Object.getPrototypeOf({}) into your console.

As we said above, all functions are objects, which means all functions must have a prototype value on it. Let’s rework the code just a little bit:

We’ve extracted the update function and moved it onto the prototype.

.

But what is the prototype, and why is this in any way helpful?

Before I explain what the new keyword does and how it relates to prototypes, let’s take a look at what the memory layout for our balls array is now:

Two ball objects, but they point at a single Ball.prototype, with a single update function on it.

There’s now only one function object for update. We could call new Ball(100) a million times, and there’d still only be one instance of the update function object. Each of these instances would point at the same Ball.prototype, which points at the one update function.

So what’s happening here? How was the prototype set?

.

What the new keyword is really doing 🔮

From the MDN description, the new keyword:

  • Creates an empty JavaScript object (like {})
  • Replaces the prototype of this object to the newed function’s prototype — remember, functions are objects, and all objects already have a prototype.
  • Passes the newly created object from Step 1 as the this variable. (This happens behind the scenes.)
  • Returns this if the function doesn't return its own object.

You can actually model this inside JavaScript (though please don’t actually use this code anywhere!)

.

Weird caveat with arrow functions

You might have noticed that I’ve only use function () { } so far in this post, and never () => {}. In most cases, arrow functions are the same as regular functions, however there’s something worth noting.

An arrow function cannot be used with the new keyword. You’ll see the following error if you try to do so:

const Ball = () => { return { speed: 0 } }; new Ball();
"Ball is not a constructor."

This is likely to discourage use of making function constructors directly, and to prefer use of the class keyword.

.

So what about classes? 🏫

Modern JavaScript (ES6, or languages like Typescript) provides the class keyword to simplify some of this process for us.

Let’s rewrite Ball using class syntax.

This is the modern, recommended way to write the Ball class. While I wouldn’t generally recommend you use JavaScript in a class heavy way, if you’re going to, you may as well follow best practice.

If you’re in a modern web browser or version of Node (since 4.3!), the class keyword is understood and supported directly. By following the intended semantics, in theory, it can also produce more optimized code under the hood (though I’ve yet to see any proof of this being taken advantage of.)

.

Why is the class keyword any better than writing it myself?

It’s better in the same way that writing C is generally considered better than writing direct Assembly code. The higher abstraction of the class keyword can wrap up some messy bits and make it harder for us to make mistakes. It helps us fall into the pit of success.

Note that if you’re targeting ES5 (“regular” JavaScript), you’ll need to use something like Babel to compile your new code down. If you run this example through Babel, you’ll see how it actually generates code similar (but at first glance, quite different) to our first examples.

The class keyword is still creating a prototype based object. You can see an example of it here.

.

To conclude ✔️

Our marketing campaign was a success! Customers love watching 1000s of balls bounce along a web-page without having to shell out for extra RAM.

Unfortunately, due to a combination of the the campaign having nothing to do with our products or services, and funds being embezzled by the CEO, the hardware store tanked and shut down in a matter of hours.

.

To actually conclude

JavaScript provides powerful object-oriented features, but we should use them with intent and understanding. JavaScript provides many powerful functional features as well, which are well worth looking into.

If you’re coming to JavaScript from another language, remember to write idiomatic JavaScript code — not to try shoehorning C++ concepts like inheritance chains in because that’s what you’re used to.

.

Further reading

Check out the following three articles for further, deeper understanding of JavaScript objects and prototypes.