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()
andbind()
- use
this
effectively in event callbacks
In this lesson, we will talk about one of the foundational mechanism of JavaScript functions, special keyword this
.
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
andsetInterval
. - 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.
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.
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 everyfunction
and it's puropse in JavaScript is to be used only inside of the funcitons.
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.
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 |
If you are wondering why this rule applies to class
es, 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.
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
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();
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();
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.
Methods call()
, apply()
or bind()
can explicity set the value of this
inside of a function.
These methods can only be used with regular funciton
and can not be used for arrow funcitons.
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()
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
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.