What is "this" (in JavaScript)?

☕ 6 min read
🏷️
  • #JavaScript
  • The this keyword in JavaScript can cause a lot of headache if you aren’t sure what it references.

    A function’s this references the execution context for that call, which is determined by how the function is called. This is the golden rule that will help you resolve the this value in any function.

    Functions can be called in four different ways in JavaScript:

    1. Implicit Binding
    2. Explicit Binding
    3. The new keyword
    4. Default Binding

    Let’s explore each of them.

    Implicit Binding

    The this keyword in a function refers to the object that invokes that function.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    let classroom = {
      teacher: "Eric",
      remark(phrase) {
        console.log(`${this.teacher}: ${phrase}`);
      }
    }
    
    classroom.remark("'this' is your new best friend!")
    // Eric: 'this' is your new best friend!
    

    In this example, this references classroom, so this.teacher resolves to classroom.teacher which resolves to Eric.

    This allows us to pass in function definitions to different objects and have the objects use the function in their context.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    function remark(phrase) {
      console.log(`${this.teacher}: ${phrase}`);
    }
    
    let classroom1 = {
      teacher: "Eric",
      say: remark
    }
    
    let classroom2 = {
      teacher: "Otha",
      say: remark
    }
    
    classroom1.say("'this' is your new best friend!");
    // Eric: 'this' is your new best friend!
    
    classroom2.say("** EPIC SPEECH **");
    // Otha: ** EPIC SPEECH **
    

    Explicit Binding

    You can also set the this context when invoking a function using the call, apply, or bind method.

    Using the call or apply Method

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    
    function remark(phrase) {
      console.log(`${this.teacher}: ${phrase}`);
    }
    
    let classroom1 = {
      teacher: "Eric",
    }
    
    let classroom2 = {
      teacher: "Otha",
    }
    
    remark.call(classroom1, "'this' is your new best friend!")
    // Eric: 'this' is your new best friend!
    
    remark.call(classroom2, "** EPIC SPEECH **");
    // Otha: ** EPIC SPEECH **
    

    When we are passing in the classroom1 and classroom2 objects to the call method, we are explicitly telling the remark function to use the objects as the this value inside the function.

    Note: The apply method works similarly except it takes in the function arguments as an array instead of a comma separated list.

    Using the bind Method

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    function remark(phrase) {
      console.log(`${this.teacher}: ${phrase}`);
    }
    
    let classroom = {
      teacher: "Eric",
    	say: remark
    }
    
    const boundRemarkFunction = remark.bind(classroom);
    boundRemarkFunction("Questions, comments, concerns?")
    // Eric: Questions, comments, concerns?
    
    console.log(boundRemarkFunction);
    // [Function: bound remark]
    
    setTimeout(classroom.say, 10, "Oh no Eric's missing!!!");
    // undefined: Oh no Eric's missing!!!
    
    setTimeout(boundRemarkFunction, 10, "Break's over everyone.");
    // Eric: Break's over everyone.
    

    The bind method makes a copy of the function that calls it, sets its this context to the object that is passed in, and returns the new function.

    Every time that bound function is called, it’ll have its this permanently set to the object with which bind was called.

    Notice how calling setTimeout with classroom.say doesn’t work as the function remark will be called by the window object which doesn’t have any teacher variable defined on it.

    But when it’s called with the boundRemarkFunction, it works because the this keyword in the function will always reference classroom.

    The new Keyword

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    
    function createClassroom(teacher) {
      this.teacher = teacher;
    // return this; <- done automatically by the 'new' keyword
    }
    
    createClassroom.prototype.remark = function (phrase) {
      console.log(`${this.teacher}: ${phrase}`)
    }
    
    const classroom1 = new createClassroom("Eric");
    classroom1.remark("What's up everyone?");
    // Eric: What's up everyone?
    
    const classroom2 = new createClassroom("Otha");
    classroom2.remark("** ANOTHER EPIC SPEECH **");
    // Otha: ** ANOTHER EPIC SPEECH **
    

    The new keyword is used for constructor calls. This allows us to link multiple objects to the same function through their prototype chain.

    The new keyword does four things:

    1. Creates a new empty object .
    2. Links the newly created object’s __proto__ to the function’s prototype.
    3. The new object is set as the this reference for that function call.
    4. Returns the newly created object automatically unless there is an explicit return statement inside the function.

    Here’s what it’d look like if you were to use Object.create to link the functions:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    function createClassroom(teacher) {
      const newClassroom = Object.create(classroomFunctionStore);
      newClassroom.teacher = teacher;
      return newClassroom;
    }
    
    const classroomFunctionStore = {
      remark: function (phrase) {
        console.log(`${this.teacher}: ${phrase}`)
      }
    }
    
    const classroom1 = createClassroom("Eric");
    classroom1.remark("What's up everyone?");
    // Eric: What's up everyone?
    
    const classroom2 = createClassroom("Otha");
    classroom2.remark("** ANOTHER EPIC SPEECH **");
    // Otha: ** ANOTHER EPIC SPEECH **
    

    Default Binding

    What happens when none of the above rules apply?

    In non-strict mode, the fallback is defined as the global object.

    In strict mode, this is set to undefined.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    
    var teacher = "Eric"; 
    
    function remark(phrase) {
      console.log(`${this.teacher}: ${phrase}`);
    }
    
    function strictRemark(phrase) {
      "use strict";
      console.log(`${this.teacher}: ${phrase}`);
    }
    
    remark("Any last questions?");
    // Eric: Any last questions?
    
    strictRemark("Oh no!!!")
    // Uncaught TypeError: Cannot read property 'teacher' of undefined
    

    The teacher variable is declared as a property on the window object. When remark is called, this.teacher evaluates to window.teacher which evaluates to "Eric".

    Note that this won’t work if teacher is declared with either let or const (see here for the reasons).

    What happens in Arrow Functions?

    The rules mentioned above don’t work in arrow functions. These functions instead use the this reference of the function they are enclosed in.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    function createRemarkArrowFunc() {
      return remarkArrowFunc = (phrase) => {
        console.log(`${this.teacher}: ${phrase}`);
      }
    }
    
    const classroom1 = {
      teacher: "Eric",
      say: createRemarkArrowFunc()
    }
    
    const classroom2 = {
      teacher: "Otha"
    }
    
    classroom2.say = createRemarkArrowFunc.call(classroom2);
    
    classroom1.say("Oh no!!!");
    // undefined: Oh no!!!
    
    classroom2.say("** THE MOST EPIC SPEECH **");
    // Otha: ** THE MOST EPIC SPEECH **;
    

    In the classroom1, when we call createRemarkArrowFunc the value of this defaults to window according to the default rule. Since we don’t have any teacher variable defined globally, this.teacher evaluates to undefined.

    For the classroom2 object’s say property, we explicitly set the this to the classroom2 object when calling createRemarkArrowFunc. The this.teacher inside the arrow function evaluates to classroom2.teacher which then evaluates to Otha.

    Note that the behavior of arrow functions can’t be overridden even by the new keyword.

    Read the “Lexical this” section here for more details.

    Further Readings