Eslam Hefnawy Serverless architect at Serverless, Inc. Co-creator of the Serverless Framework and the lead architect of Serverless Components.

Beginner’s guide to the JavaScript this keyword

5 min read 1543

Beginner's Guide to the JavaScript `this` Keyword

Understanding the fundamental concepts of a programming language can go a long way. In JavaScript, the this keyword is one such cornerstone.

The this keyword in JavaScript can be a tough nut to crack. Some beginners struggle with the wording, others with its dynamic nature.

In this tutorial, we’ll attempt to demystify JavaScript’s this keyword and, in doing so, help you practice debugging issues that would otherwise be quite complex.

What is the this keyword?

With so many things going on within just a few lines of code behind the scenes, there are two fundamental questions you must answer to execute said code:

  1. Where is the code being executed (e.g., in a function or globally)?
  2. What, exactly, is the code (e.g., an array method or an object method)?

The this keyword helps to answer where the code iss because it’s part of what’s known as the execution context. The execution context defines the neighborhood of a piece of code. Unfortunately, it’s poorly named, which leads to some confusion among new JavaScript developers.

By default, all code is executed in the global execution context, as opposed to a local execution context like a function body. Think of the global execution context as your courtyard and the local execution context as your house.

The scope is what defines the visibility/accessibility of the code within an execution context. For example, to say that the variable cats is out of scope for function catMaker() means that the function catMaker() does not have access to the variable cats because cats is not in the scope chain of catMaker(). The scope chain defines all the variables a particular execution context can access.

It’s a lot of information to take in, but you really need to understand this to understand this. If you’re still struggling to follow along, don’t sweat it — you’re not alone. Try reading it again or head to the resources section to find more helpful guides.

We made a custom demo for .
No really. Click here to check it out.

Where will you find this?

Let’s look at some places where you’re likely to encounter the this keyword.

The global execution context

The global execution context is the default execution context, and within it is a local execution context. If you were to write some code, the respective contexts would be defined as follows.

let myName = "John Doe";
// global execution context

function sayName() {
   // local execution context
   console.log(myName);
}

In the global execution context, the value of this is the same as what is known as the window object in the browser. Think of the window object as representing a tab (because it contains all kinds of fancy details about it) in a browser. To verify that this is the same as the window object in the global execution context, you can just run the following piece of code.

console.log(this === window); // prints true

Plain functions

Functions have their own execution context and scope, but if you define a function in the global execution context, the value of this will be, again, the same as the window object.

function someFunc() {
  return this;
}

someFunc() === window; // returns true

This may or may not be desirable. If you’d like to avoid this, you can enable what is known as the strict mode in JavaScript. This literally forces JavaScript to throw more errors where appropriate, ultimately yielding to code that is more predictable. When the strict mode is enabled, this will yield to undefined.

function someFunc() {
  "use strict"
  console.log(this);
}

someFunc(); // returns undefined

There may also be cases where you want to change what this is for a function to something else, more or less to change the context of that function. To do this, you can use the functions call(), apply(), and bind(). Starting with the latter, the bind() function binds a function with the value of this that you provide and returns a new function.

const obj = { 
   message: "hello world"
}

function printMessage() {
   console.log(this.message);
};

const boundPrintMessage = printMessage.bind(obj);
printMessage(); // prints undefined
boundPrintMessage(); // prints "hello world"

The bind() function is a very powerful tool that can help you create reusable code and solve some tricky situations, some of which we’ll look at later on.

If you want to avoid returning a new function bound to a value of this, you should consider using call() or apply(). call() and apply() both allow you to call a function with a value of this that you provide, except with call(), you can pass in parameters to the function, and with apply(), you pass those parameters as an array.

const user = {
 name: 'John Doe'
}

function printUser(likes) {
  console.log(`My name is ${this.name}, and I like ${likes}`)
};

printUser.call(user, 'apples')
printUser.apply(user, ['apples'])

Arrow functions

Arrow functions, also known as ES6 fat arrow functions, are almost identical to plain functions, with a few critical exceptions. For one, unlike with plain functions, the value of this does not default to the window object. You can demonstrate this by declaring an arrow function in an object.

const obj = {
  message: "hello world",
  arrowFunc: () => console.log(this.message),
  plainFunc: function() {
   console.log(this.message);
  }
}

obj.arrowFunc() // prints undefined
obj.plainFunc() // prints hello world

Because arrow functions don’t have their own value of this in this situation, it’s not recommended to use arrow functions as object methods. You might think that, since bind() allows you to change the value of this for a function, you can use bind() to avoid this behavior, but that won’t work.

const obj = {
  message: "hello world",
  arrowFunc: () => console.log(this.message),
  plainFunc: function() {
   console.log(this.message);
  }
}

const boundArrowFunc = obj.arrowFunc.bind(obj);
boundArrowFunc(); // prints undefined

call(), apply(), and bind() were introduced to allow functions to execute in a scope that you define, but the value of this in arrow functions depends on the scope it was defined in.

Classes

ES6 classes always operate in strict mode, so the value of this for classes is not the same as the window object. As you may know, though, ES6 classes are a kind of syntax sugar, so if you were to write an ES6 class in traditional function style, the value of this will be the window object.

The value of this in classes depends on how they are called. As such, there might be instances where you want to set the value of this to be that of the class’ instance.

class Person {
  constructor() {
   this.name = "John Doe"
   this.sayName = this.sayName.bind(this); // Try running the code without this line
 } 
   sayName() {
    console.log(this.name);
  }
}

const somePerson = new Person();
somePerson.sayName();
const sayName = somePerson.sayName;
sayName();

If you’re familiar with using React, you might be familiar with this pattern — called hard-binding — when writing class components. When you do not bind the value of this in your event handlers to that of the class, the value of this tends to be undefined.

How to determine what this could resolve to

We’ve gone over some of the most common cases, but, of course, there are other situations you may encounter. Refer to the tips below to help determine what this could resolve to in a given scenario.

  1. If the code is not inside a body/block, then this is the same as the window object in the browser
  2. If the code is called as an object method and expressed via dot notation, then look to the left; whatever is on the left is what this is
  3. If the code is inside a function, then this is the same as the window object when strict mode is not active. When strict mode is enabled, this is undefined, as it should be
  4. If the function is an object method, then if it’s a plain function, this resolves to the object in which the function is defined, while an arrow function would refer to the enclosing execution context
  5. If you wish to define what the value of this should be for a function, you should use call(), apply(), or bind().

Conclusion

Understanding the fundamentals of JavaScript will help you immensely when you start wrangling with complicated frameworks and libraries. It’s imperative to have a solid understanding of topics like the this keyword if you want to learn how to debug and write error-free code that doesn’t behave weirdly.

Don’t worry if you don’t get it right away — topics this complex can take a while to sink in. To achieve a clear understanding, you must write code, get a feel for the situations we described in this post, and solve the issues that arise by trial and error. That will help solidify your understanding and get you to the next step.

Resources

 

You come here a lot! We hope you enjoy the LogRocket blog. Could you fill out a survey about what you want us to write about?

    Which of these topics are you most interested in?
    ReactVueAngularNew frameworks
    Do you spend a lot of time reproducing errors in your apps?
    YesNo
    Which, if any, do you think would help you reproduce errors more effectively?
    A solution to see exactly what a user did to trigger an errorProactive monitoring which automatically surfaces issuesHaving a support team triage issues more efficiently
    Thanks! Interested to hear how LogRocket can improve your bug fixing processes? Leave your email:

    : Debug JavaScript errors easier by understanding the context

    Debugging code is always a tedious task. But the more you understand your errors the easier it is to fix them.

    LogRocket allows you to understand these errors in new and unique ways. Our frontend monitoring solution tracks user engagement with your JavaScript frontends to give you the ability to find out exactly what the user did that led to an error.

    LogRocket records console logs, page load times, stacktraces, slow network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier!

    .
    Eslam Hefnawy Serverless architect at Serverless, Inc. Co-creator of the Serverless Framework and the lead architect of Serverless Components.

    Leave a Reply