this
in JavaScript is an object. Every JavaScript function gets a copy of this (except arrow functions). Its value does not depend on where we declare it but depends on how we invoke it. There are four ways in which we can invoke a function. Function invocation, method invocation, Constructor invocation & Explicit invocation. Each of these invocations will assign different this
to the function. In this tutorial, we will dive deep into the this
and learn more about it.
Table of Contents
What is this
This
is an object to which function is bound to in the run time. The run-time binding of a function can change depending on how we invoke it. If there is no binding is found, then it defaults to the global object in nonstrict mode and undefined in strict mode.
Why This in JavaScript is so Confusing
In languages like c#, java, etc., this
always points to the current instance of the class to which method belongs. This is very intuitive and easy to grasp. The methods are bound to the class. You cannot change those bindings.
But there is one critical difference between functions in JavaScript and those in c# or Java. In JavaScript, functions are objects. They are not bound to anything. Hence you can assign them to another variable, pass them around, etc.
Now take the following example.
The obj
object declares the foo
function, it becomes the method of the obj. When we invoke the foo function using obj.foo()
it prints the value of a
(i.e. 10) from the obj
and not from the global value of a
. Here this
inside the foo
points to the obj
itself. This is exactly how this
works in C# & Java.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | a=1; obj = { a:10, foo:function () { console.log(this.a) } } obj.foo() // 10 |
In the following example, we copy foo
to another variable boo
. Now boo
also points to foo
function, but it is not associated with the obj
.
When we invoke boo, this
points to the global object rather than foo
. That is why it prints 1 instead of 10.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | a=1; obj = { a:10, foo:function () { console.log(this.a) } } boo=obj.foo //Both of these call the foo function. But get different value for 'this' obj.foo() // 10 'this' is obj boo() // 1 'this' is global object |
We can also assign the function as the property of another object. The following example attaches foo to another object obj2. When we invoke obj2.foo()
, this
of the foo
points to obj2
.
1 2 3 4 5 6 7 8 | obj2= { a:100, foo:obj.foo } obj2.foo(); // 100 |
So when you declare a function, you will never sure what would be its this
. Because it never depends on where you declare it. But entirely depends on how you invoke it. The same function invoked differently gets different value for this
.
How to identify this
To identify this we need to find out, how & where we invoke the function and whether the strict mode is enabled or not.
There are four ways in which you can invoke a function. They are
- Function Invocation
- Method Invocation
- Constructor invocation
- Explicit invocation
Each of the above Invocations sets the value of this
differently. In the case of function invocations, the Strict Mode also affects the value of this
.
This outside a function
The value of this
outside of a function is always the global object.
The global object always exists in JavaScript applications. But what value it takes depends on where we run our app.
Inside the browser, the global object is window
, while in NodeJS it is an object named global
. The value does not depend on strict mode.
For Example, the following code returns window
when run on a web browser.
1 2 3 | console.log(this) //Always window inside a browser, global inside a NodeJs |
Function Invocation or Default Binding
When we invoke a function as a standalone function (not as a property of an object), then we call that function Invocation.
In a function invocation this
is bound to the global object in “not strict” mode. But if we use “strict” mode this
is always null.
The window object is the global object in the web browser, while in NodeJs an object called global
is the global object
1 2 3 4 5 6 7 | function hello() { console.log(this==window) //Window } hello() //'this' here is window |
Invoking a Function expression.
1 2 3 4 5 6 7 | hello = function() { console.log(this==window) //Window } hello() //'this' here is window |
But in a strict mode, this is undefined
1 2 3 4 5 6 7 8 9 10 | "use strict" function hello() { console.log(this==window) //false console.log(this) //undefined } hello() //'this' here is undefined |
You can also use use strict
inside the function, but it must be at the beginning of a function.
1 2 3 4 5 6 7 8 9 10 | function hello() { "use strict" console.log(this == window) //false console.log(this) //undefined } hello() //'this' here is undefined becuase hello uses "use strict" |
Method Invocation (Implicit Binding)
The method is the function that we declare as the property of an object. When we invoke the method using the property accessor ( using dot or [] ), we call it method invocation.
In a method invocation, the object that invokes the method becomes the this
of the method.
The following codes are examples of method invocation.
1 2 3 4 5 6 7 | foo.getName(); //this == foo foo["getName"]() //this == foo boo.getName(); //this == boo |
The last level of the object becomes this
. In the following chained call foo
comes last. Hence it becomes this
.
1 2 3 | boo.foo.getName() //this == foo |
The getName
method inside foo
, boo
& bar
refers to the global getName
function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | getName = function () { console.log(this.name) } foo = { name:"foo", getName:getName } foo.getName(); //foo boo = { name:"boo", getName:getName } boo.getName(); //boo bar = { name:"bar", foo:foo, boo:boo, getName:getName } bar.foo.getName() //foo bar.boo.getName() //boo bar.getName() //bar |
Remember dot is not the only way to invoke a method. You can also make use of a bracket.
1 2 3 4 5 6 7 8 9 10 11 12 | var counter = { value: 0, increment: function () { console.log(++this.value); } }; counter.increment(); //'this' = counter counter["increment"](); //Another way to invoke a method |
Inner function loose binding
In the following example, we attach outer
function as method to obj
object. Inside the outer
function, we have inner
function.
We invoke the outer
function as method invocation (obj.outer()
). Hence obj
becomes the this
of the outer
function.
But inside the outer
function, we invoke the inner
function using the function invocation. Hence it gets the window
object as its this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | var val=10 let obj = { val:100, //property of the obj outer:function() { //'this' here is obj. // Hence we can access its property console.log(this==obj) //true console.log(this.val) //100 let inner = function() { //'this' here is window not obj console.log(this==window) //true //val comes from the golbal variable console.log(this.val) //10 } inner(); //function invocation.'this' is window } }; obj.outer(); //method invocation 'this' of outer function is obj |
Workaround for the inner function
There is a simple way by which we can make the Inner function to access this
from the outer function.
To do that create a local variable in the outer function. Name it as that
. Assign this
to that
. Now you can access the that
variable in the inner function
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | var val=10 let obj = { val:100, //property of the obj outer:function() { //'this' here is obj. Hence we can access its property console.log(this==obj) //true //define a local variable `that` and store `this` in it. let that=this; let inner = function() { //'this' here is window not obj console.log(this==window) //true //`that` comes from the parent scope console.log(that==obj) //true //use that console.log(that.val) //100 } inner(); //function invocation } }; obj.outer(); //method invocation |
Separate method from the object
In the following example, we assign obj.outer()
to a variable outer
. The outer variable contains the reference to the outer
function
When we invoke the outer using the method invocation (i.e. obj.outer()
), the obj
becomes the this
of the outer
.
But when we invoke it using the function invocation (i.e. outer()
), then the window
becomes the this
of outer
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | var val=10 let obj = { val:100, outer:function() { console.log(this==window) console.log(this==obj) console.log(this.val) } }; outer= obj.outer; //assigning to a varible obj.outer() //method invocation. 'this' of outer is obj //false //true //100 outer() //function invocation. 'this' of the outer is window //true //false //10 |
But we can force the outer
function to use the obj
as this
by using the method bind
. This is called explicit binding. We will talk about it later
1 2 3 | outer= obj.outer.bind(obj); |
Constructor Invocation or New Binding
We can also invoke a function using a new Operator. We usually use the new operator on a Constructor function to create a new object. But we can use the new operator on any function.
If the function is invoked with new keyword, then the this
is a new object
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | function hello() { //It is not window console.log(this==window) //false //it is not even hello console.log(this==hello) //false //'this' is an empty object console.log(this) } new hello(); //constructor invocation. 'this' is a new empty object |
If you invoke a function without new keyword it will be a function invocation. In that case the this
will be global object
1 2 3 4 5 | //this is a function invocation. //'this` is global object hello(); |
You can read more about constructor function and new operator
Explicit Binding
We can also set the value of this
explicitly. i.e. we pass the value of this
, when we invoke the function overriding the JavaScript generated this
. This is useful when we make use of the callback functions
There are three methods that allow us the set the this
of an object. All of them are available in the function.prototype
object.
- Call
- Apply
- Bind
Call
The call()
method invokes a function. It sets the first parameter as the this
of the function
For Example, the foo.call
method invokes the foo method. The first argument bar
becomes the this
of the foo
function
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | var a =1 function foo() { console.log( this.a ); } var boo = { a:10, foo:foo } var bar = { a: 100 }; foo(); //5 Function invovation, 'this' here is global object boo.foo() //10 Method invocation. 'this' is boo //Explicit binding foo.call(bar); //100 Sets 'this' of foo to bar and invokes it boo.foo.call(bar) //100 Sets 'this' of foo to bar and invokes it |
Apply
The apply()
method invokes a function. It sets the first parameter as the this
of the function
The apply method is very similar to the call method. They only differ in how they define the second parameter. The apply method excepts an array as the second parameter.
You can easily replace call
in the previous example to apply
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | var a =1 function foo() { console.log( this.a ); } var boo = { a:10, foo:foo } var bar = { a: 100 }; foo(); //5 Function invovation, 'this' here is global object boo.foo() //10 Method invocation. 'this' is boo //Explicit binding foo.apply(bar); //100 Sets 'this' of foo to bar and invokes it boo.foo.apply(bar) //100 Sets 'this' of foo to bar and invokes it |
Bind
The bind method allows us to change the this of a function and invoke it any time later. bind method does not invoke the function, but it returns a new function
For Example, the code s2=obj.show.bind(obj)
sets the this
of method show to obj
and stores it in variable s2
. Now we can invoke s2
anytime, it is always hardbound to obj
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | var value=10 obj = { value:100, show:function() { console.log(this.value) } } obj.show() //100 method invocation 'this' is obj s1=obj.show //function separated from the obj s1() //10 Function invocation 'this` is global object. //Using bind s2=obj.show.bind(obj) //s2 is a function now hard bound to obj s2() //100 Function invocation. but function is hard bound. hence 'this' is global object s2() //100 |
Internally bind returns a wrapper function, which uses the apply method to bind the object to this.
1 2 3 4 5 6 7 | function bind(fn, obj) { return function() { return fn.apply( obj, arguments ); }; } |
Primitive Values & Explicit binding
If we pass a primitive value to call, apply or bind, they will convert it into its object representation. For Example, the string is converted to an object into using new String()
Passing null or undefined to bind, call & apply
The bind, call & apply functions ignores the null or undefined. instead, they will use the default binding.
Exceptions
There are Exceptions to the above rules of determining this
.
Arrow functions
The arrow functions do not declare any this
. Hence if we try to access this
it will look for it in its parent scope.
You cannot even change the this
of arrow
function using apply
, call
or bind
methods. i.e. because the arrow function does not have this
For Example, we use the arrow function for the getName
method. It will return undefined because this for arrow function comes from its parent scope, which is global scope.
1 2 3 4 5 6 7 8 9 10 | var person = { firsName:'Alex', lastName:'Ferguson', getName: () => console.log(this.firsName+' '+this.lastName) } person.getName(); //undefined Method Invocation. But Since it is an arrow function 'this' is global object |
Hence it is usually not recommended to use the arrow function as the method of an object.
1 2 3 4 5 6 7 8 9 10 11 12 | var person = { firsName:'Alex', lastName:'Ferguson', getName() { console.log(this.firsName+' '+this.lastName) } } person.getName(); //Alex Ferguson Method Invocation.'this' is person |
For Example, you can refer to the This and arrow function
This in an ES6 Module
Inside an ES6 Module but outside the function this
is always undefined
. Also, ES6 Modules always runs on strict mode.
Hence the value of this
inside a function invoked using function invocation is always undefined in an ES6 Module. However, the other types of function invocation like method, constructor invocation & explicit bindings work as mentioned above.
Precedence
It also important to know the Precedence of this
binding. It is actually possible two bindings in a function call
- New binding or constructor calls
- Explicit Binding
- Method invocation or implicit binding
- Default binding
Here is some example of Precedence of this
binding
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | obj1= { name:"Bill", hello: function() { console.log(this) } } obj2= { name:"Alex", hello: obj1.hello } hello=obj1.hello; hello() //'this' = window obj1.hello() //'this' = obj //New new obj1.hello() //'this' = new object. new takes precedence new obj2.hello() //'this' = new object. new takes precedence obj1.hello.call(obj2) //this = obj2. binding has higher precedence |
Summary
Finally the summary of determining the this
- If we invoke the function with a new operator, this is always a new object
- function invoked with the call, apply & bind methods uses the first argument as their
this
. But if you pass null or undefined as the first argument, then they use the default binding. - The method invocations use the context object (i.e. object left of the dot notation)
- If all of the above fails, then use the default binding. it is null in the case of strict mode. else global object (window in browser, global in NodeJs).