Skip to content

Instantly share code, notes, and snippets.

@ross-u
Last active February 8, 2021 04:10
Show Gist options
  • Save ross-u/cee086d535d89fbff8ed2b2c3df37e78 to your computer and use it in GitHub Desktop.
Save ross-u/cee086d535d89fbff8ed2b2c3df37e78 to your computer and use it in GitHub Desktop.
Lecture - JS | Special value this (student material)

JS | special value this

Learning goals:

After this lesson, you will be able to:

  • understand the purpose of special value this
  • list the rules of special value this
  • use this effectively in functions
  • use this effectively with arrow functions
  • use call(), apply() and bind()
  • use this effectively in event callbacks

Introduction

In this lesson, we will talk about one of the foundational mechanism of JavaScript functions, special keyword this.


Why understanding this is important ?

this is one of the most misunderstood mechanisms in Javascript. Many JavaScript developers don't fully understand how this works and why it is even used.

However, this mechanism isn't actually that difficult and it has few simple rules that we will explain in the following steps.

Understanding this special keyword value is an important foundational concept used in many areas of JavaScript.

After learning how this works in JavaScript you will be able to:

  • Use functions properly as methods in OOP.
  • Use classes to create new objects and understand how a class works.
  • Successfully handle callbacks of DOM event listeners, setTimeout and setInterval .
  • Better understand frontend frameworks (React , Angular, Vue ...)
  • Better understand code of other developers such as one found in libraries, packages, frameworks.

The above mentioned will serve as foundation allowing you to effectively work with JavaScript and put you in the position of understanding the code of others and being a JavaScript developer with a good grasp of the language.

special keyword value = reserved JavaScript keyword used in a specific place, holding a value.


What is this ?

this is a special keyword value automatically defined in the scope of every function. JavaScript automatically creates this value in the scope of every function, to enable reusability of the function.

this is also commonly called a function's context, however for brevity and to avoid confusion we will always use this when talking about it.


this is a special keyword value that JavaScript automatically creates in the scope of every function.


const flight = {
  airline: 'United',
  flightNo: 'UA336',
  from: 'SFO',
  to: 'BCN',
  
  printTicket: function() {
    console.log(`Flight ${this.flightNo}: ${this.from} - ${this.to}`)
  }
}


flight.printTicket(); // --> Flight UA336: SFO - BCN

In the above example value this within the function refers to the object, effectively allowing us to access the values from the object and use them withing the function.


Where is this being used ?

Special keyword value this is always used in functions.

Developers commonly get confused and may think that it is used in objects, when in fact this is a special value that JavaScript creates inside of every function.

There is an exception which may contribute to this confusion. JavaScript doesn't throw an error if we try to access this value outside of a function.


// In the global scope
console.log(this); // ==> Window

As we can see in the example above, if we try to access this value outside of a function block JavaScript won't throw an error. However, this is natural to the functions and it's purpose in JavaScript is to be used only inside of the functions.

this is a special value that JavaScript creates inside of every function and it's puropse in JavaScript is to be used only inside of the funcitons.


Prerequisite: Function invocation

To understand how this works, we first have to understand function invocation.

Function invocation is the place in code were a function is called ( not where it's declared ).

We invoke a function by setting a () after the function name.


function example() {} //  Declaration


example(); // Function invocation, place where we call/run the function

In the above example, function example() {} is the declaration block and example() is the invocation of the function.


5 Rules of this

There are 5 rules that describe how this behaves in JavaScript functions, depending on how the function is invoked (called).

To put it in another way, the value that this holds in the function will change depending on how the function is invoked.


Rule Value of this
With new keyword (class) In a class, this reprents the new object instance created inside of the constructor.
Standalone function invocation this references the global object Window
In function methods this is the object that is on the left of the dot at time of invocation.
In ()=> arrow functions/methods Arrow function doesn't have this binding and therefore takes value of this from the scope in which it is created.
Using - call() ,apply() or bind() this value in a function can be customized using these methods

Rule 1: With new keyword (in a class)

If you are wondering why this rule applies to classes, when this is only used in functions. The reason is that JavaScript classes are special types of functions used to create new objects.

Therefore when new keyword is used during class invocation, the value of this in the constructor represents the new object instance.


class Ticket {
  constructor(flightV, fromV, toV) {
    // this = {}
    this.flight = flightV,
    this.from = fromV,
    this.to = toV
    
    // this = {flight: '...', from: '...', to: '...' }
    // return this
  }
}

const t1 = new Ticket('UA2638', 'MIA', 'BCN');
const t2 = new Ticket('UA521', 'JFK', 'SFO');

When a class is invoked with keyword new to construct a new instance of an object ( e.g. new Ticket() ) , this inside of the constructor holds the new empty object. The properties are then added to this empty object and at the end the formed object is returned.

As you can see in the above example, these steps that happen behind the scene are shown by the commented code.


Rule 2: this in standalone function invocation

When a funciton is invoked as a standalone function (nothing on the left of the dot), the value of this is the global object Window.


function printThis() {
  console.log('this ->', this);
}


printThis(); //    this ->  Window {}

If our script is running in a script mode, then the value of this in a standalone function invocation is undefined. Therefore if we execute the previous example with the strict mode enabled value of this will change.


"use strict"

function printThis() {
  console.log('this ->', this);
}


printThis(); //    this ->  undefined

Rule 3: this in function method invocation

When a funciton is invoked as a method (having the object on the left of the dot), the value of this is the object on the left of the dot at the time of invocation.


const ticket = {
  airline: 'United',
  flight: 'UA336',
  
  printTicket: function() {
    console.log(this);
  }
}


ticket.printTicket();
// { airline: "United", flight: "UA336", printTicket: ƒ }

If a funciton is invoked as a method of the nested object, the same rule applies.


const ticket = {
  airline: 'United',
  flight: 'UA336',
  
  printTicket: function() {
    console.log(this);
  },
  
  details: {
    planeType: "Airbus",
    planeModel: "A380",
    printDetails: function () {
      console.log(this);
    }
  }
}



ticket.details.printDetails();
// { planeType: "Airbus", planeModel: "A380", printDetails: ƒ }

The value of this will be the details object as it is first on the left of the dot when the method is invoked.

This way method printTicket has access to the object ticket2 via this and the nested method printDetails has access to details object.

We could expand the above example to actually print the ticket information or the details of the flight:


const ticket = {
  airline: 'United',
  flight: 'UA336',
  
  printTicket: function() {
    console.log(`FLIGHT: ${this.airline} ${this.flight}`);
  },
  
  details: {
    planeType: "Airbus",
    planeModel: "A380",
    printDetails: function () {
      console.log(`DETAILS: ${this.planeType} ${this.planeModel}`);
    }
  }
}

ticket.printTicket();

ticket.details.printDetails();

Rule 4: this in arrow functions and methods

Normal funcitons (created using function keyword) follow the above rules, but the ES6 arrow function does not.

Arrow functions => don't have the special value this inside of it's scope. We can think of arrow function being a lighter version of a regular function. When JavaScript creates an arrow function it creates it without thespecial keyword value this within it's scope.

For that reason arrow function takes the value of this from the surrounding scope (place) where it was created.

If the arrow function is not created nested inside another function or class, the value of this will be the global object (Window).


const person = {
  name: 'John',
  age: 35,
  
  regularMethod: function () {
    console.log('regularMethod  this', this)
    
    const arrowFunc = () => console.log('arrowFunc  this', this);
    arrowFunc();
    
    // arrow function will borrow `this` value from the surrounding scope
    // of `regularMethod` where it was created
  },
  
};

// What is this in the `person.myMethod` ?

person.myMethod();

Using arrow function to prevent loss of this

Certain built-in JavaScript functions and interfaces cause the loss of this value in the functions.

These are setTimeout/setInterval, event listeners and loop methods forEach/map/filter.

class Clock {
  constructor() {
    this.time = 0;
      
    this.tickRegular = function () {  
      this.time += 1;
      console.log(this.time);
    };
    
  }
};

const clockA = new Clock();

setInterval( clockA.tickRegular, 1000 ); //

In the above example setInterval is continuously invoking the method tickRegular every 1s ( 1000 milliseconds ). However the this value in the tickRegular is lost as method is called as a callback by the browser's function setInterval.

One common technique to prevent the loss of this in when using the above mentioned built-in functions is to use arrow functions or arrow methods. Let's see how we can fix the above problem by using an arrow method instead:


class BetterClock {
  constructor() {
    this.time = 0;
    
    this.tickArrow = () => {
      this.time += 1;
      console.log(this.time);
    }
    
  }
};

const clockB = new BetterClock();

setInterval( clockB.tickRegular, 1000 ); //

We can see that arrow function takes the value of this from the scope in which it was created, from the constructor, where this represents the new object instance.


Rule 5: Using call() , apply() or bind()

Methods call(), apply() or bind() can explicity set the value of thisinside of a function.

These methods can only be used with regular funciton and can not be used for arrow funcitons.

call() and apply()

Methods call() and apply() are used when we want to invoke a function once and pass it a custom value of this for that one invocation.

The first argument passed to call()/apply() will be set as this value for that one function invocation.


function printThis() {
  console.log(this);
}


const office = {
  address: 'C. Pamplona 96',
  color: 'gray'
}

const school = {
  address: '120 SW 8th St',
  color: 'white'
}


printThis(); // --> Window

printThis.call(office); //  -->  office { address: "C. Pamplona 96", color: "gray" }

printThis.call(office); //  -->  school { address: "120 SW 8th St", color: "white" }

bind()

bind() method is used to create a new function with a permanently set (bound) this value.

Method bind() creates a copy out of an existing function and sets this to the provided value.

const car = {
  brand: 'Tesla',
  model: 'S',
  details: function () {
    console.log(`${this.brand} ${this.model}`);
  }
}

car.details(); // -->   Tesla S



const macPro15 = {
  brand: 'Apple',
  model: 'MacBook Pro 15'
}


// Create a new function and bind `this` to object `macPro15`
const boundDetails = car.details.bind(macPro15);


boundDetails(); // --> Apple MacBook Pro 15
boundDetails(); // --> Apple MacBook Pro 15


// new function `boundDetails` has `this`
// permanently bound to object `macPro15`

In addition to being used to permanently bind this value, bind() can be used to set


Summary

When using keyword new with a constructor, this represents a newly created object.

A standalone function called without a leading parent object will get the value of this set to the global object.

In regular function methods, this points to the object that is on the left of the dot at the time of invocation .

Arrow functions do not have it's own this, but take the value of this from the surrounding scope where they were created. If arrow function is not nested inside a class or another function the value of this will be global scope.

We can use call , apply or bind to change the function'sthis value.


Extra Resources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment