While learning JavaScript there are many roadblocks like closures
, asynchronous programming
, this
keywords, etc. These are the parts of JavaScript that are difficult to understand for a newbie in JavaScript, but once you learn them you can leverage everything JavaScript provides. So today I will be talking about one such superpower and that is this
keyword.
So without further delay let's get to it. Before we go into this
let's talk about an analogy to understand scope in JavaScript.
An Analogy to Understand Scope in JavaScript
There is a 10 story building and there is a policeman who is looking for a criminal in this building. Now let's see the two ways of how he finds the criminal or what if he does not find the criminal at all. 😨😨
(i) The policeman enters the building and starts looking for the criminal on the ground floor. If he does not find the criminal on the ground floor he goes to the first floor and if he finds the criminal on the first floor he sends him back to police station otherwise he keeps going up one floor at a time until the criminal is caught. And if he does not find the criminal then he reports back to the police station that criminal could not be found in the building.
(ii) This one is interesting 😋 There is a way by which the policeman can be directly shot onto any one of the floors and whoever he finds on that floor is the criminal now and there is no way to change that. Such a dynamic way of finding the criminal, isn't it? 😂
What do you think which approach can have bad consequences??
Obviously the second approach, as in the first approach what if the real criminal is on another floor and the police just caught a civilian.😫
So with a dynamic mindset now let's try to understand the two different scopes in JavaScript: static or lexical scope
and dynamic scope
.
Lexical Scope
In our first approach, the scope of finding the criminal is lexical
. The policeman first looks on the ground floor and if he does not find the criminal then he goes one floor up and so on. That's exactly how lexical scoping works in JavaScript.
const outer = 12;
function lexical(){
let inner = 21;
console.log(inner, outer);
}
lexical(); // 21 12
In the above snippet lexical
function first looks for inner variable
and it finds it on its own floor. But for outer
it finds it own one floor up. So that's how the lexical scope works in JavaScript.
Dynamic Scope
Let's see in code how dynamic scope works.
So here we see instead of alerting 'Gotcha Joe' we got 'Gotcha undefined' when we click on the box. Why is that ?? Why instead of catching the Joe
, our policeman (eventHandler function) got undefined
. This is because our eventHandler function uses this
and depending upon where our handler function is called or invoked this
can take different values. There are ways to fix it, I will get to them in a while, I promise.
So far we have understood the scoping in JavaScript and seen how using this
can result in unexpected outcomes.
Now let's talk about this', and how to figure out the values of
this` in our code.
What this
actually is?
In JavaScript this
is an object
inside a function or in a scope which can be any value depending upon how the function is defined or how the function is called.
Let's understand this in different contexts -
case 1 - default this binding
javascript
function printThis() {
console.log(this);
}
printThis(); // in browser it can be window object or in node global object
In the above snippet, the default value of this
is what is available inside the function when we try to access it. Now this default value depends on how and where we are calling the function. Here we are calling our function in the global context.
case 2: implicit binding
let obj = { name: 'Joe', handler: function() { console.log(this.name); } } obj.handler(); // Joe
Here we are implicitly providing the context for this
to the function. The value on the left of .
is the value of this
for our handler function. In this case, it is the object obj
itself.
case 3: binding this
with apply
function printThis() { console.log(this.name); } printThis.call({name: 'Joe'}); // Joe
The call
method takes the first argument as the value of this
that you want to pass to the function and invokes the function. Here we have passed an object with a property name on it. This object will be used as this
inside our printThis
function.
case 4: the new
keyword
The new
keyword in JavaScript is used to create a new instance of a class using class syntax or constructor functions. You can read more here in my last post
Understanding Prototypal Inheritance.
So these were different ways of how this works in the JavaScript world. In our click handler example, I promised that I will be telling you different ways of fixing that code so that instead of catching undefined
we will be able to catch 'Joe'.
Here is the modified code for that on Codepen
So we have introduced two new ways of handling this
- fat arrow functions
-
bind
method
Let's understand them one by one
fat arrow functions
ES6 introduced a new syntax for writing functions. Though it looks like just syntactic sugar, there is more to it than just new syntax. Fat arrow functions handle this
very differently than normal functions. In fat arrow functions this
is scoped lexically just like any other variable. We have created fat arrow function version of our previously defined normal functions and now inside our eventHandlerArrowFunction
the value of this
is the instance of the class itself no matter where the function is called.
bind
method
Just like the call
method ES5 also introduced the bind
method. The difference between the two is that bind
takes the value of this
as the first argument and returns a new function with this
bound to it. Where call
invokes the function with the provided value of this
along with other arguments.
In our click handler function, we want to pass the reference of the function to addEventListener
instead of invoking the function. Therefore we used the bind
method there.
So with this, I hope you understood how the mighty this
works in JavaScript.
Top comments (2)
In the first code example shouldn't the output of lexical() be 21 12 ?
Yeah, made a typo. Fixed now