What is this
in javascript?
this
keyword refers to an object, the object that is executing the current part of our Javascript code.
Why is it so complicated to understand?
We might feel overwhelmed as there are quite a number of ways to manipulate this
by providing it a different context.
this
binding has nothing to do with where a function is declared but it has everything to do with how a function is invoked.
There are 4 main Rules which we can make use of, to determine what this
in your code represents.
E.D.I.N - Stands for Explicit binding, Default binding, Implicit binding and new Binding.
(There is no EDIN concept, its just my way to remember these concepts)
Rule 1.Implicit Binding:
Implicit Binding is achieved when the function that is executed is called with a context.
Example:
const obj = {
name: 'Dev',
getName : function() {
console.log(this.name);
}
}
obj.getName(); // Dev
obj.getName()
- Here we call the
getName()
function of the objectobj
withobj
as the context. - Since
obj
hasname
property,this.name
would give out dev as the output.
What if your function is nested within objects?
Example:
const obj = {
name: 'Dev Child',
getName : function() {
console.log(this.name);
}
}
const parentObj = {
childObj: obj,
name: 'Dev Parent'
};
parentObj.childObj.getName(); // Dev Child
- The last level parent before any function call is the context for that function.
- In our case
obj
is thethis
forgetName()
function.
The Fat Arrow
function catch:
var name = 'Global Dev';
const obj = {
name: 'Local Dev',
getName : () => {
console.log(this.name);
}
};
obj.getName() // Global Dev 😈 😈 😈
- The problem here is
this
binding has been done for thegetName
arrow function and it takes Lexicalthis
which is Gloabl in this case. - So calling the function with impicit binding takes lesser priority than Arrow function.
2.Default Binding:
Default binding is whenever the function is called without any context.
A common mistake occurs while destructuring a function from an object which has this
context in it.
var name = 'Global Dev';
const obj = {
name: 'Local Dev',
getName : function() {
console.log(this.name);
}
};
const { getName } = obj;
getName(); // Global Dev 🤔🤔🤔
- Here we have destructured the
getName
function out of the objectobj
. - Then, we have called it without any context
- It means the function execution here is happening with Global context.
- So while execution if it encounters any
this
in the code that will try to resolve its value by checking window object. - We have 'Global Dev' value assigned to
name
inwindow
object sothis.name
resolved towindow.name
which is 'Global Dev'. - Note: The same would have resulted in returning
undefined
instrict
mode.
How to Overcome this?
By using Explicit binding
3.Explicit Binding:
Explicit binding is a process of specifying what this
object is, while calling the function. It is usually done with the help of the famous Js trio call
, apply
and bind
.
var name = 'Global Dev';
const obj = {
name: 'Local Dev',
getName: function() {
console.log(this.name);
};
};
const { getName } = obj;
const newContext = {
name: 'New Dev'
};
// Explicit binding takes priority over Default binding
getName.call(newContext); // New Dev
// Explicit binding takes priority over Implicit binding
obj.getName.call(newContext); // New Dev
It is clear from the above that, Explicit bindings take priority over Implicit or Default Binding.
But does it have a Higher priority than Arrow function's this
binding.
No!
var name = 'Global Dev';
const obj = {
name: 'Local Dev',
getName: () => {
console.log(this.name);
}
};
const newContext = {
name: 'New Dev'
};
//Arrow function's bind took priority over Explicit binding.
obj.getName.call(newContext); // Global Dev
Priority Ordering:
Arrow Function > Explicit Binding > Implicit Binding > Default Binding
4.new
Binding:
If the function is called with new
operator in the prefix then the newly constructed object is the this
reference here.
function MyName() {
this.name = "Local Dev";
}
MyName.prototype.getName = function() {
console.log(this.name);
}
// `new` binding
const name_instance = new MyName();
console.log(name_instance.name); // Local Dev
// Implicit Binding
name_instance.getName(); // Local Dev
- Here we have defined our instance variable
name_instance
which is formed fromnew
opertor operating on factory functionMyName
. - All references to
this
inMyName
function refers to the newly created object instancename_instance
All our Rules from 1 to 3 applied to this instance (name_instance
):
// Default Binding
var name = "Global Dev"
const {getName} = name_instance;
getName(); // Global Dev
// Implicit Binding
name_instance.getName(); // Local Dev
// Explicit Binding
name_instance.getName.call({ name: 'New Dev'}); // New Dev
// Arrow Binding
MyName.prototype.get_Arrow_Name = () => {
console.log(this.name);
}
name_instance.get_Arrow_Name(); // Global Dev
React Classes:
class App extends React.Component {
constructor() {
this.handle_three = this.handle_three.bind(this);
}
handle_one() {
console.log(this);
}
handle_two = () => {
console.log(this);
}
handle_three() {
console.log(this);
}
render() {
return (
<React.Fragment>
{/* Default binding */}
<div onClick={this.handle_one}></div>
{/* Arrow Function */}
<div onClick={this.handle_two}></div>
{/* Expicit Binding at constructor*/}
<div onClick={this.handle_three}></div>
</React.Fragment>
)
}
}
- Handlers on JSX elements will call the function declared.
-
handle_one
attachment results in calling the function with no context(Default binding). This results inundefined
because React ensures Default binding results inundefined
rather than a Global. -
handle_two
attachment results in calling the function with the newly created Intsance's (Current App Class's instance's) context. -
handle_three
attachment results in explicit binding to provide value forthis
at the constructor.
Hope you guys have Enjoyed this Article 😄
Reference: Kyle Simpson's this & Object Prototypes
Thats all Folks!!!
Top comments (1)
Very informative post! I just wanted to mention that in the case of events handled by
addEventListener
, if you pass an object that implements#handleEvent
, it will be called withevent
as its argument, and it will be implicitly bound to the object passed in toaddEventListener
. If you call any other functions on the object within the body ofhandleEvent
, they will also be implicitly bound to the object, too! This saves memory by preventing you from having to use#bind
or the arrow syntax for those functions, which both create copies of the function in question,. In circumstances where you have a large number of objects, this can save you a ton of overhead!