this
Whenever we invoke any function in JavaScript, a brand new execution context is created at the run-time and an object is created which is referred to as this.
this refers to the current function execution context 1 and its value inside a function depends on the style of function invocation.
The value of
thisdepends on how the function is called and is evaluated during the run-time.
It is a very important and confusing concept. A lot of questions can be asked, which can be answered correctly only if you know the concept very well.
For example -
For the same method, value returned is different in both cases because value of this depends on how the function/method is invoked and not on where it is declared
1
2
3
4
5
6
7
8
9
10
11
12
var a = 2;
var obj = {
a: 33,
foo: function() {
return this.a;
}
}
console.log(obj.foo()); // 33
var bar = obj.foo;
console.log(bar()); // 2
We need to check that how the function is invoked, and based on that, we need to apply one of the four rules to find out the value of this inside any function -
- Rule 1: Standalone function invocation - behaviour is different in
strictornon-strictmode - Rule 2: Invoked as an object method
- Rule 3: Invoked using
call(), apply() - Rule 4: Invoked with
newkeyword
With the introduction of ES6, the value of this is now also dependent on the way a function is created -
- Function type -
arrow functionsornormal functions - Classes -
non-static methodorstatic method
Now, let us look at those rules in detail with the help of examples.
Rule 1: Standalone function invocation
It is the most common way of invoking a function.
According to this rule, if the function gets invoked without any object reference or any other bindings. The value of this will be equal to -
windowinnon-strictmodeundefinedinstrictmode
non-strict mode
Here, we have created a function foo and, when we invoke that in the non-strict mode, this resolves to the global object.
1
2
3
4
5
function foo() {
console.log(this); // [window]
}
foo();
Similarly, If declare a variable var a = 2;, in the global scope, it is same as adding a variable a inside a global scope. So, when we invoke foo in this case it prints 2 which was assigned to var a.
1
2
3
4
5
6
7
function foo() {
console.log(this.a); // 2
console.log(window.a); // 2, [because, this = window]
}
var a = 2;
foo();
In these examples, Rule 1 is applied because we are calling foo() without any bindings, and also, nothing is present on the left side of the foo() call.
strict mode
If we try to execute the same code in strict mode, we will always get this as undefined.
1
2
3
4
5
6
7
8
'use strict';
function foo() {
console.log(this); // undefined [because, this !== window]
console.log(window.a); // 2, [because, `a` is created on global window object]
}
var a = 2;
foo();
Rule 2: Invoked as an object method
If we invoke a function as a method(function defined inside an object is known as method), then the value of this inside a method is always equal to the current object.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var myObj = {
a: 2,
// foo is declared as a method
foo: function() {
// returning the current value of `this`
return this;
}
}
// Way 1
console.log(myObj.foo()); // myObj{} -> {a: 2, foo: [f]}
console.log(myObj.foo() === myObj); // true
console.log(myObj.foo().a); // 2
// Way 2
// We can also store the object into a variable
// returned by invoking the method
var bar = myObj.foo();
console.log(bar); // myObj{} -> {a: 2, foo: [f]}
console.log(bar === myObj); // true
console.log(bar.a); // 2
This is how the invocation of an object method works, but in interviews this question can be asked differently, as shown in the example below -
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Way 3
var myObj = {
a: 2,
// foo is declared as a method
foo: function() {
// returning the current value of `this`
return this;
}
}
var bar = myObj.foo;
// invoked as a standalone function(Rule 1)
console.log(bar()); // [window(non-strict mode), undefined(strict mode)]
console.log(bar === myObj); // false
console.log(bar.a); // undefined
Everything is same as that of the previous example, only on line 11, we are storing the reference of the function in a variable instead of invoking it.
Question 1: What will get printed in the console(line 13, 14, 15)?
You can tell the interviewer that -
When we try to invoke that method at a later point(line 12), then instead of pointing to the object(obj), it will be equal to global object or undefined.
Why?
Because, In case of object method whenever, we store the reference of the function in a variable, the binding of the object is lost and function will be invoked as a standalone function (Rule 1) at a later stage.
Question 2: How can we fix this code?
After storing the reference to the method, we lost the binding to this. Now to fix this binding issue we need to use bind() -
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var myObj = {
a: 2,
// foo is declared as a method
foo: function() {
// returning the current value of `this`
return this;
}
}
var bar = myObj.foo.bind(myObj);
// invoked as a standalone function(Rule 1)
console.log(bar()); // {a: 2, foo: [f]}
console.log(bar() === myObj); // true
console.log(bar().a); // 2
There is a simpler way to identify the current execution context 1 in case of Rule 1 and Rule 2.
You can check if there is something present on the left-hand side of the function invocation and -
- If yes, then
thiswill be equal to thatobject
1
2
3
4
5
6
7
8
var obj = {
a: 2,
foo: function() {
console.log(this); // {a: 2, foo: [f]}, this === obj
}
}
obj.foo();
- Otherwise if nothing is present on the left-hand side then
thiswill be equal to eitherglobal objectorundefined
1
2
3
4
5
function foo() {
console.log(this); // window
}
foo();
Rule 3: Invoked using call(), apply()
This is also known as Explicit binding.
By using call() or apply() we can change the value of execution context 1(this) at the run time.
1
2
3
4
5
6
7
function foo() {
return this;
}
console.log(foo()); // this === window
console.log(foo.call({a: 2})); // this === {a: 2}
console.log(foo.apply({a: 4})); // this === {a: 4}
When we invoke the function foo()
- As a standalone function(line 5), it returned
thiswhich was equal to theglobal window object - Using
call() or apply()(line 6, 7) and passed a different object to it which acts as an execution context1 forfoo(), it returned the same object{a: 2}
Rule 4: Invoked with new keyword
If a function is invoked using a new keyword(as a constructor function), then the value of this inside that function is alway equal to an object.
1
2
3
4
5
6
7
8
9
10
// invoked as a constructor function
function Foo() {
this.a = 2;
this.b = 4;
console.log(this); // {a: 2, b:4}
console.log(window.a); // undefined
console.log(window.b); // undefined
}
var bar = new Foo();
If the same function is invoked as a normal function, then this would be equal to window object and all the properties (a and b) will be created on window object.
1
2
3
4
5
6
7
8
9
10
// invoked as a normal function
function Foo() {
this.a = 2;
this.b = 4;
console.log(this); // window
console.log(window.a); // 2
console.log(window.b); // 4
}
var bar = Foo();
ES6
Now, before we get into the interview questions. I would like to discuss the behaviour of this on some of the new concepts introduced in ES6 and later versions.
this in arrow functions
Arrow functions from ES6 do not have their own this value instead they borrow this from their lexical scope and use that value.
In the following example, bar() is an arrow function(line 8) and invoked as a standalone function, but Rule 1 will not be applied here because Rule 1 can be applied only on normal function.
this inside bar() is equal to the obj because bar() is an arrow function and it borrows this from the enclosing scope and in this case it borrows this from the function foo().
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// example 1 - with arrow function
var obj = {
a: 2,
foo: function() {
console.log(this); // obj
// arrow function
bar = () => {
console.log(this); // obj
console.log(this.a); // 2
}
bar();
}
}
obj.foo();
If I try to write the same function as a normal functions, then you will observe the differences in terms of their behaviour.
Here, bar() is a normal function and invoked as a standalone function. So Rule 1 will be applied here and accordingly this inside bar will point to the global window object.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// example 2 - with normal function
var obj = {
a: 2,
foo: function() {
console.log(this); // obj
// normal function
function bar() {
console.log(this); // window
console.log(this.a); // undefined
}
bar();
}
}
obj.foo();
Cheat sheet
There is a trick that can be used to answer most of the input-output questions in an interview. You just need to check two things -
- Is there any
explicit bindingis present on the function invocation -fn.call(), fn.apply(), fn.bind() - Is there any
objectpresent on theleft sideof the function invocation -obj.method()
If none of them is present, than you need to look if the code is in the strict mode or non-strict mode.
- In non-strict mode -
this = window - In strict mode -
this = undefined
You can refer to this flow chart for more clarity -