I am lost with this — JavaScript
If you are here , then at some point you may also have lost your “this”…so let’s discuss about it more
Table of Content
- What is “this” in JavaScript?
- Problems with “this”
- Four Magical Rules
- Playing with arrow function
- Conclusion
What is “this” in JavaScript ?
“this ”usually refers to the execution context or In the global execution context (outside of any function), this
refers to the global object whether in strict mode or not.
For Example:
function foo () { return anything; }foo();
In above example “this” or scope will refer to global window that is
this === window // true;
but not in every case. How ? we’ll discuss later.
but till now everything seems fine with “this ”… why everyone howl so much about lost in “this” ? let’s Discuss the problems
Problems with “this”
Use Case 1:
function useCase(name) {
this.myName = name; function returnMe() {
return this; //scope is lost because of the inner function
}
return {
returnMe : returnMe
}
}let baz = new useCase('this is lost');
baz.returnMe().myName // undefined
In this use case, we are getting undefined in the end… why ? because of inner function. “returnMe” function is returning its own scope hence we can not get the “myName” variable.
Use Case 2:
function getSomeContent(filePath, callbackFn, scope) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
callbackFn.call(scope, xhr.responseText);
}
}
xhr.open("GET", chrome.extension.getURL(filePath), true);
xhr.send();
}var test = "hey !";
getSomeContent("test.js", function(data) {
alert(test);
});
In this scenario, test will be resolved as you’d expect it, but the value of this
might be different. Again… we lost it.
Solution ? before that lets understand the bindings
Four Magical Rules or KS Rules
Thanks to kyle simpson for this. I am a great follower of him, so In his own way, lets discuss about bindings
- Default Binding
- Implicit Binding
- Explicit Binding
- New Binding
To understand the concept of “this”, forget everything and just remember we have to look only for “call site”. We don’t care about function declaration now.
Default Binding ( First Rule )
function foo() {
// we passed global execution context with default binding,
this.faz = 'whopiee';
// this is referring to window object hence faz will get declare in global context
this.baz(); //
}function baz () {
console.log(this.faz); // hence we are referring to same this
}
foo(); // current context is global
our first rule gets applied when we are calling function as standalone function invocation like foo(); in above example
In Above example
Since we are calling with global site scope hence we are using the global scope which is our first rule to understand this
Implicit Binding ( Second Rule )
function bam() {
console.log( this.a );
}
var obj = {
a: 18,
bam: bam
};
obj.bam(); // 18
What to see now ? “Call Site” , call site now says. Hey compiler ! I am giving you the reference. Which is obj , Hence in this case our scope will refer to current object. In above example, our current context is obj hence value of a will be 18. so now if you see the call site with object.function, here we will go with second rule which is implicit binding.
With implicit binding as we just saw, we had to mutate the object in order to include a reference on itself to the function, and use this property function reference to indirectly (implicitly) bind this
to the object.
But, what if you want to force a function call to use a particular object for the this
binding, without putting a property function reference on the object?
Explicit Binding ( Third Rule )
For the third rule, if you ever see .call() , .bind() or .apply(), Don’t look for anything else it is our explicit binding. These 3 functions are known as function bindings
function baz() {
console.log(this.value);
}
let obj = {
value : 'could be anything'
};
baz.call(obj); // could be anything
calling baz with explicit binding by baz.call(..)
allows us to force its this
to be obj
.
function foo(param) {
return this.value + param;
}var obj = {
value: 2
};var baz = function() {
return foo.apply( obj, arguments );
};var boo = baz(1); // 1 2
console.log(boo); // 3
The most typical way to wrap a function with a hard binding creates a pass-thru of any arguments passed using apply and any return value received.
function baz(param) {
return this.value + param;
}var obj = {
value: 1
};var bindFunction = baz.bind(obj);var result = bindFunction(4); // 5
bind(..)
will return a new function that is hard-coded to call the original function with the this
context set as you specified.
New Binding ( Fourth Rule )
our last rule, So now again go to “call site” and see if we are using constructor pattern or new keyword. Label it as 4th Rule
let baz = new foo(...);
what actually new does in code behind ? so whenever we write our code using new keyword 4 things happen in code behind which is :
- A new empty object is created {}
- Newly created object gets register to prototype
- the newly created object is set as the
this
binding for that function call - And will automatically return the newly constructed object.
Example ? lets see:
function bam(param) {
this.value = param;
}
var baz = new bam(2);
// above 4 things will happen and this of bam will get returnconsole.log(baz.value);
In above Example , We’ll get the this/scope of function bam() into baz.
By calling bam(..)
with new
in front of it, we've created a new object and set that new object as the this
for the call of baz(..)
. So new
is the final way that a function call's this
can be bound. We'll call this new binding.
In my next article, we will see the magic of arrow function to solve the problem of this
I hope that you’ll like my article. Please praise me with your feedback :)