Introduction

Hi, I'm Elie!

The keyword 'this'

Objectives

  • Define what the keyword 'this' is
  • Understand the four ways to always figure out what the keyword 'this' is
  • Try as hard as possible not to use the word "this" in a sentence

So what is 'this'?

  • A reserved keyword in JavaScript
  • Usually determined by how a function is called (what we call 'execution context')
  • Can be determined using four rules (global, object/implicit, explicit, new)

1 - Global Context

console.log(this) // window

function whatIsThis(){
    return this
}

function variablesInThis(){
    // since the value of this is the window
    // all we are doing here is creating a global variable
    this.person = "Elie"
}

console.log(person) // Elie

whatIsThis() // window

When 'this' is not inside of a declared object

Strict Mode

"use strict"

console.log(this) // window

function whatIsThis(){
    return this
}

function variablesInThis(){
    // since we are in strict mode this is undefined
    // so what happens if we add a property on undefined?
    // let's see what happens when we call the function...
    this.person = "Elie"
}

variablesInThis() // TypeError, can't set person on undefined! 

whatIsThis() // undefined

When we enable strict mode and we are not inside a declared object

2 - Implicit/Object

// strict mode does NOT make a difference here

var person = {
    firstName: "Elie",
    sayHi: function(){
        return "Hi " + this.firstName
    },
    determineContext: function(){
        return this === person
    }
}

When the keyword 'this' IS inside of a declared object

person.sayHi() // "Hi Elie"
person.determineContext() // true

Nested Objects

var person = {
    firstName: "Colt",
    sayHi: function(){
        return "Hi " + this.firstName;
    },
    determineContext: function(){
        return this === person;
    },
    dog: {
        sayHello: function(){
            return "Hello " + this.firstName;
        },
        determineContext: function(){
            return this === person;
        }        
    }
}

What happens when we have a nested object?

person.sayHi() // "Hi Colt"
person.determineContext() // true

// but what is the value of the keyword this right now?
person.dog.sayHello() // "Hello undefined"
person.dog.determineContext() // false

3 - Explicit Binding

Choose what we want the context of 'this' to be using call, apply or bind

NAME OF METHOD PARAMETERS INVOKE IMMEDIATELY?
Call thisArg, a, b, c, d , ... Yes
Apply thisArg, [a,b,c,d, ...] Yes
Bind thisArg, a, b, c, d , ... No

Fixing Up With Call

var person = {
    firstName: "Colt",
    sayHi: function(){
        return "Hi " + this.firstName
    },
    determineContext: function(){
        return this === person
    },
    dog: {
        sayHello: function(){
            return "Hello " + this.firstName
        },
        determineContext: function(){
            return this === person
        }        
    }
}

person.sayHi() // "Hi Colt"
person.determineContext() // true

person.dog.sayHello.call(person) // "Hello Colt"
person.dog.determineContext.call(person) // true

// Using call worked! Notice that we do NOT invoke sayHello or determineContext

Using Call in the Wild

var colt = {
    firstName: "Colt",
    sayHi: function(){
        return "Hi " + this.firstName 
    }
}

var elie = {
    firstName: "Elie",
    // Look at all this duplication :(
    sayHi: function(){
        return "Hi " + this.firstName 
    }
}

colt.sayHi() // Hi Colt
elie.sayHi() // Hi Elie (but we had to copy and paste the function from above...)

// How can we refactor the duplication using call? 

// How can we "borrow" the sayHi function from colt 
// and set the value of 'this' to be elie?

Let's examine a very common use case

Using Call in the Wild

var colt = {
    firstName: "Colt",
    sayHi: function(){
        return "Hi " + this.firstName 
    }
}

var elie = {
    firstName: "Elie"
}

colt.sayHi() // Hi Colt
colt.sayHi.call(elie) // Hi Elie 

// much better! 

Solution

What about Apply?

var colt = {
    firstName: "Colt",
    sayHi: function(){
        return "Hi " + this.firstName 
    },
    addNumbers: function(a,b,c,d){
        return this.firstName + " just calculated " + (a+b+c+d);
    }
}

var elie = {
    firstName: "Elie"
}

colt.sayHi() // Hi Colt
colt.sayHi.apply(elie) // Hi Elie 

// well this seems the same....but what happens when we start adding arguments?

It's almost identical to call - except the parameters!

colt.addNumbers(1,2,3,4) // Colt just calculated 10
colt.addNumbers.call(elie,1,2,3,4) // Elie just calculated 10
colt.addNumbers.apply(elie,[1,2,3,4]) // Elie just calculated 10

// What differences do you see?

And what about bind?

var colt = {
    firstName: "Colt",
    sayHi: function(){
        return "Hi " + this.firstName 
    },
    addNumbers: function(a,b,c,d){
        return this.firstName + " just calculated " + (a+b+c+d);
    }
}

var elie = {
    firstName: "Elie"
}

The parameters work like call, but bind returns a function with the context of 'this' bound already!

var elieCalc = colt.addNumbers.bind(elie,1,2,3,4) // function(){}...
elieCalc() // Elie just calculated 10
// With bind - we do not need to know all the arguments up front!

var elieCalc = colt.addNumbers.bind(elie,1,2) // function(){}... 
elieCalc(3,4) // Elie just calculated 10  

Bind in the wild

var colt = {
    firstName: "Colt",
    sayHi: function(){
        setTimeout(function(){
            console.log("Hi " + this.firstName)
        },1000)
    }
}

Very commonly we lose the context of 'this', but in functions that we do not want to execute right away!

var colt = {
    firstName: "Colt",
    sayHi: function(){
        setTimeout(function(){
            console.log("Hi " + this.firstName)
        }.bind(this),1000)
    }
}

Use bind to set the correct context of 'this'

colt.sayHi() // Hi undefined (1000 milliseconds later)
colt.sayHi() // Hi Colt (1000 milliseconds later)

4 - The 'new' keyword

function Person(firstName, lastName){
    this.firstName = firstName
    this.lastName = lastName
}

We can set the context of the keyword 'this' using the 'new' keyword - it does quite a bit more as well which we will discuss further when we talk about OOP

var elie = new Person("Elie", "Schoppik");
elie.firstName // "Elie"
elie.lastName // "Schoppik"

Recap

  • The keyword 'this' is a reserved keyword in JavaScript and its value is determined at execution
  • It is either set using the global context, object binding, explicit binding, or the new keyword
  • When set in the global context in a function, it is either the global object (window if in the browser) or undefined (if we are using strict mode)
  • To explicitly set the value of the keyword 'this', we use call, apply, or bind
  • We can also use the 'new' keyword to set the context of 'this', which we will discuss when we talk about Object Oriented Programming