The this
keyword in JavaScript has often been a source of much confusion for beginners to the language. Some of this confusion stems from the fact that this
in JavaScript is treated differently as compared to in other languages like in Java or self in Python.
Understanding this
is absolutely imperative in order to understand more advanced concepts in JavaScript or to read and write JavaScript code, which is why we shall spend this article trying to clarify what this really means in JavaScript.
We shall spend much of the article learning about this
with reference to functions in JavaScript, which is why we shall first look at a fact about JavaScript functions that will help us do this better.
Functions, in JavaScript, are essentially objects. Like objects they can be assigned to variables, passed to other functions and returned from functions. And much like objects, they have their own properties. One of these properties is this
.
The value that this
stores is the current execution context of the JavaScript program.Thus, when used inside a function this
‘s value will change depending on how that function is defined, how it is invoked and the default execution context.
Note : this
always holds the reference to a single object, that defines the current line of code’s execution context.
Before we delve further into how this
behaves in functions, let us look at how it behaves outside them:
Global Context:
A line of code written outside a function is said to belong to the global context and the value of this
in this global context is the same as the global object.
For example, if you opened up your browser console and typed the following lines into it, and press return/enter:
console.log(this)
You would see the Window object being logged into the console.This is because the global object, in a browser run-time such as Chrome’s run-time, is the Window object.
Inside a function, however, the global context might no longer be present and the function may have its own defined context and therefore a different value of this
. To understand that, let us turn our attention back to functions :
Functions, in JavaScript can be invoked in multiple ways :
1.Function invocation
2.Method invocation
3.Constructor invocation
Function invocation refers to the process of invoking a function using its name or an expression that evaluates to the function object followed by a set of opening and closing first brackets(inclusion of the brackets indicates that we are asking the JavaScript engine to execute the function immediately).
For instance:
<!DOCTYPE html>
<html>
<body>
<script>
function doSomething() {
// do something here
}
// function invocation
doSomething();
</script>
</body>
</html>
this
inside the doSomething function, if it is invoked through the function invocation as above, has the value of the global object, which is the window object in the browser environment:
<!DOCTYPE html>
<html>
<body>
<script>
function doSomething(a, b) {
// adds a propone property to the Window object
this.propone = "test value";
}
// function invocation
doSomething();
document.write(window.propone);
</script>
</body>
</html>
Output:
test value
However, this is not always the case. If the doSomething()
function were running in strict
mode, it would log undefined
instead of the global window object.This is because, in strict mode(indicated by the line : 'use strict';
), the default value of this, for any function object is set to undefined instead of the global object.
For example :
<!DOCTYPE html>
<html>
<body>
<script>
function doSomething() {
// enable the strict mode
'use strict';
// logs undefined
document.write(this + '<br>')
function innerFunction() {
// Also logs undefined, indicating that
// strict mode permeates to inner function scopes
document.write(this)
}
innerFunction();
}
// function invocation
doSomething();
</script>
</body>
</html>
Output:
undefined
undefined
Functions, when defined as fields or properties of objects, are referred to as methods.
<!DOCTYPE html>
<html>
<body>
<script>
let person = {
name : "John",
age : 31,
logInfo : function() {
document.write(this.name + " is " + this.age + " years old ");
}
}
// logs John is 31 years old
person.logInfo()
</script>
</body>
</html>
Output:
John is 31 years old
In the above code example, logInfo()
is a method of the person object
and we invoked it using the object invocation pattern.
That is, we used the property accessors to access the method which was part of the object.
Such an invocation requires the use of an expression that evaluates to the object which our method is a part of, and a property accessor(Eg : person.logInfo()
) followed by a set of opening and closing parentheses.
It is essential to understand how function invocations
and method invocations
differ.
This in turn will help us understand what the this
context might be in any given function, because in each of these invocations, the value of this
is different.
Inside such a method, that has been invoked using the property accessors, this
will have the value of the invoking object, that is this
will point to the object that was used in conjunction with the property accessor to make the call.
For example :
<!DOCTYPE html>
<html>
<body>
<script>
let add = {
num : 0,
calc : function() {
// logs the add object
document.write(this + ' ')
this.num
+= 1;
return this.num;
}
};
// logs 1
document.write(add.calc() + '<br>');
// logs 2
document.write(add.calc());
</script>
</body>
</html>
Output:
[object Object] 1
[object Object] 2
In the above example, calc()
is a method of the add
object and is therefore called using the method invocation rules in lines 9 and 10.
And we know, when method invocation patterns are used, the value of this
is set to the calling object.
Inside this calc()
method, the value of this is set to the calling object, which in our case is add
. and thus we can successfully access add
‘s num
property.
However, let us know look at one major confusion point:
What happens to this
in a function nested inside a method of an object?
<!DOCTYPE html>
<html>
<body>
<script>
let add = {
num : 0,
calc : function() {
// logs the add object
document.write(this + ' ')
function innerfunc() {
this.num += 1;
// logs the window object
document.write(this + ' ');
return this.num
} return innerfunc();
}
};
// logs NaN
document.write(add.calc() + '<br>');
// logs NaN
document.write(add.calc());
</script>
</body>
</html>
Output:
[object Object] [object Window] NaN
[object Object] [object Window] NaN
Let’s try to understand what just happened.
When we call calc()
in lines 14 and 15 we are using method invocation which sets this
to add
in calc()
. This can be verified using the log statement in line 4.
However, innerfunc()
is called from within the calc()
method using a simple function invocation(line 11 ). This means, inside innerfunc()
this
is set to the global object, which does not have a num
property, and hence the NaN outputs are obtained.
How do we solve this issue?How can we retain the value of this
from the outer method inside the nested function?
One solution is to assign the this
value from the outer function to a variable to be used in the nested function like so:
<!DOCTYPE html>
<html>
<body>
<script>
let add = {
num : 0,
calc : function() {
// logs the add object
document.write(this + ' ')
// using thisreference variable to
// store the value of this
thisreference = this;
function innerfunc()
{
// using the variable to access the
// context of the outer function
thisreference.num += 1;
// logs the add object
document.write(thisreference + ' ');
return thisreference.num;
}
return innerfunc();
}
};
// logs 1
document.write(add.calc() + '<br>');
// logs 2
document.write(add.calc());
</script>
</body>
</html>
Output:
[object Object] [object Object] 1
[object Object] [object Object] 2
Other solutions to this problem involve using bind(), call() or apply()
, which we will soon look into.
Constructor invocation is performed when new
keyword is followed by an function name, and a set of opening and closing parentheses(with or without arguments).
For example: let person1= new People(‘John’, 21);
Here, person1
is the newly created object and People
is the constructor function used to create this object.
The constructor invocation is one of the several ways of creating objects in JavaScript.
What exactly happens when we use the new
keyword is conjunction with a function name?
There are essentially five steps involved in creating a object through this method.Let use study them with the following example:
<!DOCTYPE html>
<html>
<body>
<script>
let people = function(name, age) {
this.name = name;
this.age = age;
this.displayInfo = function() {
document.write(this.name + " is " + this.age + " years old");
}
}
let person1
= new people('John', 21);
// logs John is 21 years old
person1.displayInfo();
</script>
</body>
</html>
Output:
John is 21 years old
instance of
the function name used with new
(i.e : people(name, age)
). In other words, it sets the constructor
property of the object to the function used in the invocation(people(name, age)
).people
) to the newly created object, thus ensuring that that this object can inherit all properties and methods of the constructor functionthis
gets the value of the newly created object used in the call.person1
One should remember that the new
keyword is essential for correctly setting the context inside the constructor function.Without new
we would have a normal function invocation and hence this
might be incorrectly set to the global object even inside the constructor.
#javascript