Understanding JavaScript this keyword
If you want to become a programmer, no matter which specific field you choose, you will almost certainly need to tackle JavaScript. It has evolved from humble beginnings into a true powerhouse. It now covers front end, backend, mobile, games, desktop apps, and even machine learning!
Now, before you become a JavaScript master and branch out, you must learn the fundamentals. The this keyword should probably be number one on your learning list.
What actually is this?
Simply put, this is a pointer, a way to reference which object called a function or method in JavaScript code. In layman’s terms, this is what’s to the left of the dot.
This has two main uses. Let’s explore them with some examples.
Constructing objects with this
function Book(title, author, genre) { this.title = title; this.author = author; this.genre = genre; } const book1 = new Book("Lord Of The Rings", "J.R.R. Tolkien", "Fantasy"); const book2 = new Book("Tools Of Titans", "Tim Ferriss", "Self-help"); console.log(book1); console.log(book2);
The above code produces the following output:
Here, we defined a constructor function from which we make book objects which take a title, an author, and a genre as parameters.
A constructor function is an object “blueprint” function from which we can create objects of the given name (Book in our case).
We created an object called myBook, and passed the required parameters (called arguments when we call or invoke a function, as opposed to parameters when we define it).
At the end we logged myBook, and got back the object with its corresponding properties. This way, each new book object that we create gets the passed values assigned to it.
Accessing properties with this:
const girl = { name: "Sarah", age: 26, introduce() { console.log( "Hi! " + "I'm " + this.name + " and I'm " + this.age + " years old." ); }, };
Here we defined a variable called girl which holds an object with name and age properties, and an introduce function.
The function logs an introductory sentence using the object’s properties, accessing them with this.wanted_property.
This and scope in JavaScript
By default, when used in the global scope, meaning not inside a defined function or object, this will refer to the global object. In the browser, window is the global object.
When you declare some primitive data (strings, numbers, booleans…), an object, or a function in JavaScript, they all get attached to the global (window) object.
Anything that you write “in the open”, you can read as if there’s a window. written before it. Let’s take a look at this code snippet:
function returnThis() { return this; }
First we define a function called returnThis in the global scope that returns the value of this. Then we call it by writing returnThis()
.
Since it’s globally scoped, we can look at it as window.returnThis()
, which returns to us what’s to the left of the dot.
Object functions a.k.a. methods
If we now define a new object that has the same function inside of it (called an object method) and we call it once again, we will get that object (denoted by the curly braces) returned to us.
const object = { returnThis() { return this; }, }; object.returnThis();
The gotcha of this and it’s three solutions
If a function is not directly tied to an object, the this inside of the function will refer to the global object.
const weirdBehaviorObject = { directlyTiedFunction() { return this; }, directlyTiedOuterFunction() { return function indirectNestedFunction() { return this; }; }, };
Let’s run this in the browser and see what we get:
We defined an object with one function returning this again, and another one which returns an inner function, which then returns this.
When we want to call the indirectNestedFunction, we first write ()
once to call the outer function which returns the inner.
Then we call the inner with another ()
which returns the window.
This is one of JavaScript’s mysterious behaviors, and it gives way to some unexpected and unwanted surprises.
Luckily, there are ways to solve this issue. The first one is by making a closure, which is how it was done pre-ES6. The second way is by using arrow functions, and third is by using the bind method, which is closely related to the call and apply methods.
"Career Karma entered my life when I needed it most and quickly helped me match with a bootcamp. Two months after graduating, I found my dream job that aligned with my values and goals in life!"
Venus, Software Engineer at Rockbot
#1 Solving this with closures
A closure is a persistent memory store, which means the variable doesn’t disappear when the function finishes executing. It is created by the JavaScript engine when a nested function is referencing a variable that’s defined in an outer function so it has access to it even when the outer function finishes running. Here’s what it looks like with our this example:
const objectWithClosure = { closureFunction() { const self = this; return function enclosedFunction() { return self; }; }, };
We defined an object with a closureFunction, in which we defined a variable called self to store our this value. Since the closureFunction is directly tied to the containing object, the value of this refers to it. Now, in our enclosedFunction, this would usually refer to the window object. Since we used a closure to access this from the containing function, we are able to preserve the calling object as it’s value.
#2 Solving this with arrow functions
The second solution is using arguably one of the best features that came with ES6, which are arrow functions.
Arrow functions represent a more concise syntax for writing functions. In addition to that, they will look one level up in scope to look for this, meaning in the function it’s contained in.
So if we define our object like so:
const arrowFunctionObject = { outerFunc() { return () => this; }, };
We can see that the arrow function returns the actual object, and not the window.
That’s because it looked up to the outerFunction that called it, where this refers to the arrowFunctionObject. So, arrow functions have a kind of built-in closure.
Nested arrow functions
Of course, this is JavaScript, so here’s another gotcha. It’s something you’ll probably never encounter outside of a technical interview, but that’s enough reason to know it.
What happens if we nest an arrow function inside a regular function, and again inside another function? It looks like this:
const arrowNestedInRegularFunctionObject = { outerFunction() { return function regularFunction() { return () => this; }; }, };
Our object has an outerFunction which, when called, returns the inner, regularFunction.
We then call regularFunction and it returns the innermost arrow function.
Now, when calling it, it returns the window object.
An arrow function will look up only 1 level up in scope. Since the regularFunction that contains it is not directly tied to our object, the window is returned instead.
If we, however, nest an arrow function inside an arrow function, inside a regular function, we will get the expected result, which is the actual containing object.
const nestedArrowFunctionObject = { outerFunction() { return () => { return () => this; }; }, };
In this case the inner arrow function looks up to the outer one. The outer is also an arrow function so it looks up one more level. The regular outerFunction is directly tied to the calling
object, and as a result, the object is what gets returned as this.
#3 Solving this with the bind method
Lastly, the third solution is using the bind method. The bind method is used to explicitly declare what this should refer to. It gets the object passed as an argument, and binds it to the function’s this keyword.
const arrowNestedInRegularFunctionObject = { outerFunction() { return function regularFunction() { return () => this; }.bind(arrowNestedInRegularFunctionObject); }, };
In this example, we chained bind to the function that would normally return the window object as this. We pass it the object that we want referenced as this as an argument, and our problem is solved.
This is not that hard when you understand JavaScript
Phew! That was quite a dive. Now, you might ask, why all the hassle and confusion with this?
The reason is that, unlike everything else in JavaScript, this is dynamically scoped. That means that the JavaScript engine defines what this represents at runtime when the code is executed, by looking at where from, and who is calling it.
In contrast, everything else in JavaScript is lexically scoped, meaning that its value is defined where the variable is defined, when the code is written.
Thanks to this, we can create an object “blueprint” once, and dynamically create objects with different property values like we saw in our book example.
We can also make methods that expose and manipulate an object’s properties with this.wanted_property.
We can work around JavaScript’s quirks, and command it’s behavior confidently, and produce expected results.
Now that you understand this in JavaScript, as well as a few other fundamental concepts, you can go and experiment, practice, and more confidently take steps towards JavaScript mastery.
You got this!
About us: Career Karma is a platform designed to help job seekers find, research, and connect with job training programs to advance their careers. Learn about the CK publication.