NOTE: These are notes from Colt Steele's The Web Developer Bootcamp (from before the 2021 update)
NOTE: There are more notes about this subject from another course here
this
keyword
Intro
- foundation for OOP
- it is a reserved keyword in JavaScript
- usually determined by how a function is called (what we call 'execution context')
- can be determined using 4 rules
- global
- object/implicit
- explicit
- new
Global Context
- the broadest rule of them all
this
IS NOT inside of a declared object (i.e. there has not been an object defined which contains the keywordthis
--- the keywordthis
is 'in the wild')- in this case it refers to the global object, which in the browser is the window object
- every object you create in the global scope is attached to the window object
let person = 'Elie' window.person // 'Elie' window.person === person // true
- every object you create in the global scope is attached to the window object
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 ==> we can use it outside the
// function --- this is bad practice --- variables that we want to use in multiple
// functions should be declared at the top of the code even if they aren't assigned until later
this.person = 'Elie';
}
console.log(person); // 'Elie'
whatIsThis(); // window
Global with Strict
- when strict mode is enabled, which you can do by adding
"use strict"
, the value ofthis
when inside functions is undefined
"use strict"
console.log(this); // window
function whatIsThis () {
return this;
}
function variablesInThis () {
// since we are in strict mode this is undefined
this.person = 'Elie';
}
variablesInThis(); // TypeError, can't set a person on undefined
whatIsThis(); // undefined
Implicit/Object
- when
this
IS inside of a declared object, the value ofthis
will always be the closest parent object
// strict mode does not make a difference here
let person = {
firstName: 'Elie',
sayHi: function () {
return 'Hi ' + this.firstName; // closest parent object is person ==> `this` is the
}, // person object
determineContext: function () {
return this === person;
}
}
person.sayHi(); // "Hi Elie"
person.determineContext(); // true
Nested Objects
let person = {
firstName: 'Colt',
sayHi: function () {
return 'Hi ' + this.firstName;
},
determineContext: function () {
return this === person;
},
dog: {
sayHello: 'Hello ' + this.firstName, // dog is closest parent object
determineContext: function () {
return this === person;
}
}
}
person.sayHi(); // "Hi Colt"
person.determineContext(); // true
person.dog.sayHello(); // "Hello undefined" --- undefined because dog doesn't
// have a firstName property
person.dog.determineContext(); // false --- `this` is refering to dog not person
Explicit Binding
- choose what we want the context of
this
to be using call, apply, or bind - these can only be used on functions
- call and bind take infinite number of parameters, while apply only takes 2
- use bind when we want full control over what
this
will refer to --- it will have precedence over call and apply
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
Call
- using call to fix the nested objects (like in the above example)
let person = { firstName: 'Colt', sayHi: function () { return 'Hi ' + this.firstName; }, determineContext: function () { return this === person; }, dog: { sayHello: 'Hello ' + this.firstName, determineContext: function () { return this === person; } } } person.sayHi(); // "Hi Colt" person.determineContext(); // true person.dog.sayHello.call(person); // "Hello Colt" --- now `this` is the person // object so `this.firstName` now has the value // "Colt" instead of undefined person.dog.determineContext().call(person); // true --- now that `this` is person the dog // object's determineContext function becomes true
- call is commonly used to avoid code duplication
- BAD example with duplication:
let colt = { firstName: 'Colt', sayHi: function () { return 'Hi ' + this.firstName; } } let 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 duplicate the whole 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?
- GOOD example without duplication:
let colt = { firstName: 'Colt', sayHi: function () { return 'Hi ' + this.firstName; } } let elie = { firstName: 'Elie' } colt.sayHi(); // "Hi Colt" elie.sayHi.call(colt); // WRONG --- elie doesn't have a sayHi function and `this // would be calling "Colt" instead of "Elie" colt.sayHi.call(elie) // "Hi Elie" --- called colt with the sayHi function and // then called `this` as elie
- BAD example with duplication:
Apply
- almost identical to call - except for the parameters
let colt = {
firstName: 'Colt',
sayHi: function () {
return 'Hi ' + this.firstName;
},
addNumbers: function (a, b, c, d) {
return this.firstname + ' just calculated ' + (a + b + c + d);
}
}
let elie = {
firstName: 'Elie'
}
colt.sayHi(); // "Hi Colt"
colt.sayHi.apply(elie); // "Hi Elie" --- same so far because no parameters
// ...what happens when we start adding arguments?
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"
Bind
- the parameters work like call, but it returns a function with the context of
this
bound already
let colt = {
firstName: 'Colt',
sayHi: function () {
return 'Hi ' + this.firstName;
},
addNumbers: function (a, b, c, d) {
return this.firstname + ' just calculated ' + (a + b + c + d);
}
}
let elie = {
firstName: 'Elie'
}
let elieCalc = colt.addNumbers.bind(elie, 1, 2, 3, 4); // returns a function definition to us
elieCalc(); // "Elie just calculated 10"
// with bind, we do not need to know all the arguments up front
let elieCalc2 = colt.addNumbers.bind(elie, 1, 2); // this is called partial application
// because we don't need all the
// parameters to the function when we bind
// it --- we only need to know what we
// want the value of `this` to be
elieCalc2(3, 4); // "Elie just calculated 10"
- bind commonly used to set the context of
this
for a function to be called at a later time --- this commonly happens with asynchronous code (i.e. code that doesn't run line by line)
let colt = {
firstName: 'Colt',
sayHi: function () {
setTimeout(function () {
console.log('Hi ' + this.firstName); // while `this` is in a parent object, because
}, 1000) // setTimeout is called at a later time `this` does
} // not refer to the parent object --- it refers to
} // the global object (the window object)
colt.sayHi(); // "Hi undefined" (1000 milliseconds later)
// call and apply execute immediately so we need to use bind with the setTimeout function, which
// won't execute until later
let colt = {
firstName: 'Colt',
sayHi: function () {
setTimeout(function () {
console.log('Hi ' + this.firstName);
}.bind(this), 1000) // inside the colt object `this` refers to the colt
} // object, so we are binding the correct value of
} // `this` to what we want when the function inside
// setTimeout is called
colt.sayHi(); // "Hi Colt" (1000 milliseconds later)
// can get the same result by passing colt to the bind method, but more commonly you will see it
// with `this`
the new
keyword
- we can set the context of the keyword
this
using thenew
keyword (will be discussed further in notes about OOP) - when
new
is used a new object is created ---new
is used with a function, and inside the function definitionthis
refers to the new object that is created
function Person (firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
let elie = new Person ('Elie', 'Schoppik'); // the `new` keyword is used, therefore the references
// to `this` inside the function are refering to the
// new object created, which is the elie object
elie.firstName; // "Elie"
elie.lastName; // "Schoppik"