What does the `new` keyword do “under the hood” in JavaScript?
In this article, we will talk about the different topics that will lead us to understand better what thenew
keyword does in JS.
- What is the
__proto__
property and its role when it comes to objects and the prototypal chaining - What’s the difference between
prototype
and__proto__
- Where in our code do we take advantage of
__proto__
andprototype
and the prototypal chain - With all the previous knowledge, we will be able to understand the
new
keyword and how do the different pieces relate to it
The tweet which you can see below is asking whether all Objects have the prototype property [by default]. It sparked a lot of discussions, different answers, and people wondering if what they think is correct or not.
Let me try and answer it in the best way I can. Let’s start with some related topics that will help us to clear the confusion around __proto__
, [[Prototype]]
, and prototype
. That will lead us to understand what the new
keyword does in JavaScript under the hood.
JavaScript is a prototypal language
One of the cool things about JavaScript is that you can write an application using different programming paradigms such as functional programming and object oriented programming (OOP). The difference between OOP languages such as Java, C#, C++, and the rest of the gang, is that JavaScripts OOP is just syntactical sugar. Under the hood, it makes use of the prototypal chain to allow us as developers to write our application in an OOP style.
Bundling up functions and objects together
Suppose the application we want to create is an e-commerce application. One of the entities that exist in the application is a customer. As a customer, I can add an item to my cart, remove an item, and also checkout/purchase the items that I hold.
Of course, you will have more than one customer registered in your application, and you want all of them to share these functionalities, the ones that we have just defined. Also in the future when our application grows we start having more use cases that we need to add to our customer functionalities.
What’s one way to bundle up multiple functions in one place in JS and save it to be used later again and again? You guessed it, in an Object!
Function are first class citizens in JavaScript
Because functions in JavaScript are first class citizens (objects), it allows us to store functions in a variable, and even in objects as properties which are referred to as methods.
In JavaScript, a function is both a function and an object. As soon as you declare a function in JavaScript it gets an object attached to it directly. You can call it a “function-object combo”. If we use the parentheses myFunc()
we are going to access the function form, and if you use the dot notation myFunc.something
we will access its object form.
The benefit of functions being first class citizens is that we can pass them as arguments to a function, return them from other functions, but also assign them as methods of an object. So let’s start bundling up the customer’s functionality into an object so we can assign it to all of our customers.
const customerOne = {}customerOne.name = "Qasim";
customerOne.items = [];customerOne.addItem = function (item) {
customerOne.items.push(item);
}
customerOne.removeItem = function (item) { ... }
customerOne.checkout = function () { ... }
As you can see this is tedious to do. You will need to repeat the above code for each customer of your app. Imagine how many developers you have to hire behind the scenes to adjust the above code to create a new customer object for every new user.
Thanks to JavaScripts prototypal feature, we can do this in a better way.
Using the prototypal chain to share functionality
There are different ways to create an object in JS, and the one that we will be focusing on in this section is the Object.create()
method. The create
method is a built-in method on the Object
that whatever you pass into it as an argument will return an empty object. But here is the twist, there is a bonus feature when using the Object.create()
that we can get access to, which we will get to it very soon.
So let’s create our first two customers and give them the functionalities that we decided upon as the first use cases of our app using Object.create()
.
function customerCreator(name, items = []) {
var newCustomer = Object.create(customerFunctionalities);
newCustomer.name = name;
newCustomer.items = items;
return newCustomer;
}var customerFunctionalities = {
addItem: function(item) { this.items.push(item); },
removeItem: function(item) { ... },
checkout: function() { .... }
}var customerOne = customerCreator("Qasim", []);
var customerTwo = customerCreator("Rawan", []);customerOne.addItem({
name: "You Don't Know JS Yet",
price: "28.30"
});
This seems a lot better than our last example, do you agree?
We are now able not only to create a new customer by just invoking a function, but also attaching different functionalities to them all at one go. Also we have an increase in memory performance and that is because the functions that are attached to the customer objects (methods) are declared once, and all of our customer objects have a link to the functionalities and not directly defined on the objects customerOne
and customerTwo
.
A link? Remember the bonus feature that I mentioned from using Object.create()
?
Let’s break down what exactly happened in the above code. To make it easier and clearer, let’s act like we are the JavaScript runtime, and let’s see how does JavaScript executes the above code in a simplified way.
Let’s run the program
function customerCreator(name, items = []) {
var newCustomer = Object.create(customerFunctionalities);
newCustomer.name = name;
newCustomer.items = items;
return newCustomer;
}var customerFunctionalities = {
addItem: function(item) { this.items.push(item); },
removeItem: function(item) { ... },
checkout: function() { ... },
}....
On line 1 we defined the function customerCreator
. When JavaScript starts reading our program (parsing phase) it looks at declarations. Both function declarations and variable declarations, and stores them in the memory.
Function decelerations are different than function expressions
var myFunc = function () { ... }
. Function expressions are seen as variables that have been assigned a function as a value, so JavaScript treats them a bit differently, and that is during the parsing phase (first phase of running a JS program, before the execution) it treats them as variables. Won’t go any deeper on this topic, but something that you might want to read more on about at a later point. At the end, function decelerations and function expressions are identical in the execution phase, so this doesn’t matter much to us right now.
On line 8, we declared a variable customerFunctionalities
with an object that holds different properties. The properties of customerFunctionalities
are assigned to the function decelerations we passed. Let’s try and visualise how does the global memory of our program looks like at lines 1–8.
As you can see in the above visualisation, we have two things that are stored, a function definition customerCreator
and a variable that holds multiple properties customerFunctionalities
. So far so good, but we still haven’t reached the line where we are calling a function.
Things will get more interesting in the next few lines where we really see the power of Object.create()
method.
....
var customerOne = customerCreator("Qasim", []);
...
We finally hit a line where we are invoking a function! We have a variable declared named customerOne
that is assigned to the result of calling the customerCreator
function definition with the arguments "Qasim"
and []
.When we call/invoke a function in JavaScript, an execution context is created.
An execution context basically is the environment where the function code is being run. The execution context also has its own local memory where it holds the arguments, variables and any other decelerations that are in the functions code during the time it’s being executed.
The thread of execution now enters the execution context of calling customerCreator("Qasim", [])
and starts executing line by line until it reaches the end of the function (no more code to run), or if it hits a return
statement.
Before executing the first line of the customerCreator
function, JavaScript takes care of the arguments that have been passed to the function and stores them in its local memory using the parameter names name: Qasim
and items: []
.
On the first line of customerCreator
we declared a variable that is assigned to the result of invoking the Object.create
method with the argument of customerFunctionalities
function definition var customerOne = customerCreator(“Qasim”, []);
.
As mentioned earlier, whatever (object or null) is passed as an argument to Object.create
it always returns an empty object, but also we get a bonus feature. Object.create
returns an empty object and assigns the passed argument to the objects __proto__
hidden property.
As soon as you create an object using Object.create()
method an empty object is returned, as well a hidden property is assigned to it called __proto__
. You can console.log
the returned object and see the __proto__
property, and it should show you that it is referencing customerFunctionalities
. This property is also called [[Prototype]]
in the JS specification ECMA-262, it’s a link to the reference of the passed object to Object.create where it is stored in the memory. You can think of it as a bond or link.
At the end of the customerCreator
, we return out the value of what is stored inside of newCustomer
which is an object that contains the name
and items
properties as well as the __proto__
property that has a bond/link to the customerFunctionalities
reference that is stored in the global memory.
This is still quite a bit of work. We still have to create two function, one that will return our customer
object and another that stores the functionalities we would like our object to link up to and access. We can automate all of this even further.
The “not-so-magical” property of a function — prototype
There is something important that I have left out form the previous sections. When I mentioned how a function in JavaScript is a first class object and it’s a “function-object combo”. As soon as a function is defined and the object is attached to it, a property with the name of prototype
is set as a property in its object form. This is done by default by JS on every function that is defined. The default value of prototype
is just an empty object.
This gives us a place where we can store more functions that we can create a bond to it and access the functions that we have defined, just like we did using the Object.create()
method.
Since prototype is just an object, we can assign it properties.
function customerCreator(name, items) {
....
}customerCreator.prototype.addItem = function (item) {
this.items.push(item);
}
The “new” keyword comes to the rescue
Finally, after we cleared up all the misconceptions and have a good grasp on how objects and functions behave as well as the prototypal chain, we can finally move on to the last section of this article, and to answer the main question: What does the `new` keyword do “under the hood” in JavaScript?
The answer is very simple. It basically does three things, that we have been doing manually all this time in the previous sections. It will create the customer object for us, attach the functionalities and return the newly created customer object.
Let’s see how our code will look like using the new
keyword and visualise what is happening under the hood.
function customerCreator(name, items) {
this.name = name;
this.items = items;
}customerCreator.prototype.addItem = function (item) {
this.items.push(item);
}customerCreator.prototype.removeItem = function (item) {
...
}customerCreator.prototype.checkOut = function () {
...
}var customerOne = new customerCreator("Qasim", []);customerOne.addItem({
name: "You Don't Know JS Yet",
price: "28.30"
});
Let’s execute the program…
On the following line var customerOne = new customerCreator(“Qasim”, []);
a new execution context is created. Even though there is a new keyword before calling the function, JavaScript still treats it as a function invocation, but with a twist.
We still haven’t seen anything new right? The function declaration customerCreator
is stored in the global memory and the addItem
,removeItem
and checkout
are set in the prototype
property of the function object as method. Now the new
keyword comes to play.
The new
keyword does three things automatically.
- Sets the
this
variable in the functions local memory to an empty Object.this
is called an implicit parameter, and it exists in every function that is invoked. - Sets the
__proto__
property of thethis
to the reference ofcustomerCreator
function objectprototype
property. - Returns the implicit parameter
this
. Notice how we don’t have anreturn
statement in thecustomerCreator
that is because this is done automatically by thenew
keyword.
- The arguments that were passed to customerCreator has been stored in the newly created execution context
name: Qasim
anditems: []
. - The implicit parameter that everything function has by default has been assigned as an empty object at first
- The
__proto__
property was set to hold the link (reference) to thecustomerCreator
prototype object. - In
customerCreator
the first two lines of the function is to assign thethis
object to have two propertiesthis.name
andthis.items
. These properties are assigned to the arguments that were passed"Qasim"
and[]
. This is not whatnew
automates, this is something that we have written. - The final thing that happens is the
this
implicit parameter is returned. Since the function invocation is set to the right of an assignmentvar customerOne = new customerCreator("Qasim, []);
it is returned and assigned to thecustomerOne
object that is stored in the global memory.
Alright, so we got things set up, our new customer object has the link to the functionalities that we require. How about now we continue with how the lookup process that happens when customerOne invokes one of the functions that are sitting in the custommerFunctions.prototype
object property.
When invoking customerOne.addItem()
JS looks inside of the object that is to the left hand side of the .
(dot) which in this case is customerOne
- Looks in the
customerOne
object and doesn’t find the methodaddItem
— it doesn’t panic yet - Looks into the
__proto__
property ofcustomerOne
object and see that it has a link to thecustomerCreator.prototype
which where it findsaddItem
- Creates a new execution context and invokes the method
addItem
- In the new execution context, the
this
implicit parameter is set to the left hand side objectcustomerOne
. This happens every time we call a method by using the dot notation, whatever is on the left side of the dot becomes thethis
implicit parameter
Summary
- Functions are first class citizens in JavaScript. They can be passed as arguments, assigned to variables, assigned as properties of an object, and can be returned by another function (Higher Order Function)
- All functions have an object that you can together you can call them “function-object combo”. The object is created as soon as you defined a function in your code.
- Every defined function has a property in it’s object form that is called
prototype
that is set automatically by JavaScript at the time of definition. - You call/invoke the function part using the parenthesis
myFunction()
and you can access its object form using the dot notationmyFunc.something
. Object.create()
ALWAYS creates an empty object, and if it is passed an argumentObject.create(myFuncStore)
it sets its__proto__
property to have a link/reference to the passed argument.__proto__
and[[Prototype]]
are the exact same thing. The first is the actualObject
property name, the latter is the name used to reference it in the ECMA-262 specification- The
new
keyword does three things. Sets thethis
implicit parameter in the functions local memory to an empty Object. Creates a link from the implicit parameterthis.__proto__
property to the functionsprototype
. And finally it returnsthis
. - When invoking a method using the dot notation (
myObj.objMethod()
) in the execution context thethis
implicit parameter is set to the object that is on the left hand sidemyObj
always.
I hope this was helpful to many of you, and sparked your interested to dive deeper into JavaScript.
Follow me on twitter if you enjoy this type of content @varqasim
Shoutout to Will Sentance for coming up with the “function-object combo” phrase