At a glance š
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!
- 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
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.
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.
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:
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?
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?
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.
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:
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?
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!)
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:
This is likely to discourage use of making function constructors directly, and to prefer use of the class
keyword.
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.)
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.
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.
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.
Check out the following three articles for further, deeper understanding of JavaScript objects and prototypes.