Disclosure
Webquestions.co is a participant in the Amazon Services LLC Associates Program, an affiliate advertising program designed to provide a means for us to earn fees by linking to Amazon.com and affiliated sites. As an Amazon Associate I earn from qualifying purchases.this
keyword.
A notorious topic for beginners and experienced programmers alike.
Though ES6 brought some changes to this topic, it is still a must to master in order to become a great programmer.
In this article I will explain the this in JavaScript function with examples, and what ES6 brought to change the behavior.
In this article I will take a unique approach.
Since understanding this
is best through examples, I will explain a principle in one line, and then we are going to practice it via examples.
Related:
- 11 resources that will help you learn Web Development in 2018
- Essential ES6, ES7 and ES8 interview questions.
- JavaScript project for beginners: modular multiplication around a circle with HTML canvas
this
prior to ES6 (ES5 and below)
"this
in JavaScript function is set by the call or defaults to another value if not set."
Take a second to think about that.
this in JavaScript function is set by the call. Sometimes, it is not being set by any call, and it defaults to a different value. That is a big part of the source of confusion with the topic.
In JavaScript like JavaScript, there are some irregularities, I will address them later.
For the moment, let's continue with the definition above in mind - this
is set by the call, if not set, it will default to other value.
example 1: simple function call from within an object.
var object = {
a: "some text",
b: function() {
console.log(this);
}
}
object.b();
//{a: "some text", b: ƒ}
You can clearly see that it was an object that called the function b
. Therefor, this
will refer to object
.
The object sets the value of this
to point to it.
example 2: object inside an object.
var obj = {
a: "text",
b: {
c: function() {
console.log(this)
},
d: "some value"
}
}
obj.b.c();
//Result: {c: ƒ, d: "some value"}
In this example, we call the function c()
.
The object that is calling c()
is object b
. not obj
. this
will refer to the calling object, in this case, b
.
example 3: calling a function from the window object.
function callMe() {
console.log(this);
}
callMe();
//{window}
In this example, it seems that callMe() isnt called by any object. It is a stand alone function.
Declaring a function like that, when the function is declared outside of any object, actually declares the function as a window object property. Try it in your browser / JS playground (JSBin, JSfiddle).
It is as if we called window.callMe()
. That is why the this
reference points to the window
object that calls the function.
example 4: calling a function from within a function.
function outer() {
function inner() {
console.log(this);
}
return inner();
}
outer();
//{window}
In this example, we got the same result. The window
object.
The inner()
function is not called by any other object. Therefor, it will default to the window
object.
example 5: calling a function from within a function. First function is declared inside an object.
What do you think will happen here? Take a moment to think about that.
var obj = {
callMethod: function () {
function inner() {
console.log(this);
}
return inner();
}
}
obj.callMethod();
JavaScript caveat: In this case this
will refer to the window object, even though obj invoked the method.
It is something you need to remember, that is just how JavaScript works.
When inner()
function is declared, this
inside inner()
loses its reference and defaults to the window
object.
For now, that is the take away point out of this example: No matter how deep your are in objects, when a function is invoked and the this
was not set by the call, it will default to the window object. In this case, inner()
is invoked without setting the this
to any value by the call.
example 6: declaring an object method using a function that was lexically declared outside of the object.
function outSide() {
console.log(this);
}
var obj = {
someProp: "Text",
callMe: outSide
}
obj.callMe();
Here we assigned the function outSide()
as a value for the property callMe
.
When we call it, the result will be {someProp: "Text", callMe: ƒ}
since obj is calling the method and it doesn't matter that the invoked function was lexically declared outside, under the window object.
The call set the value of this
to point to obj
since the latter called the function.
example 7: That's a tricky one. Similar to example 4 but this time what returns is an object invoking a function.
What do you think will happen?
function outer() {
function inner() {
var innerObject = {
method: function() {
console.log(this);
}
}
return innerObject.method();
}
return inner();
}
outer();
outer()
is invoked. It returns the invoked inner()
.
The invoked inner()
has an object in its scope. The object contains a method. The invoked inner()
returns the object calling its method.
The result: {method: ƒ}
will be logged to the console.
Why is that if we saw that a function within a function will default to the window
object (JavaScript caveat)?
because here, the most inner function returns an object that invokes a function. And since a this
in a function will refer to the calling object, {method: ƒ}
is returned. Another way to look at it is the innerObject
sets the this to refer to the caller, itself.
"strict mode
changes the way this
behaves"
strict mode
was introduced in ES5.
Among other changes the feature adds, there is a change in the behavior of this
reference.
example 8: invoking a function from within a function. Similar to example 4, but now, in strict mode
"use strict";
function outer() {
function inner() {
console.log(this);
}
return inner();
}
outer();
//undefined
Now we can see that unlike example 4, this
will not default to window object.
If not specified by the call, this
will default to undefined.
That is the major difference for that matter.
To conclude ES5 and prior:
this
is set by the call.
Every function has its own this
. Either set by the call or by default behavior.
this
will refer to the invoking object, if none is present to set this, it will default to the window object.
strict mode changes the default value of this
when not set by the call.
As I mentioned, there are some caveats and solutions for them.
I will address them all in later section.
Here is a table to summarize the possible this
values in the above cases:
this will refer to: / mode | No Strict mode | In Strict mode |
---|---|---|
Set by call | The calling object. |
The calling object. |
Not set by the call | window object |
undefined |
this
in ES6 and beyond
ES6 introduced arrow functions.
Arrow functions are here to solve some of the mess above and retain the this
of the surrounding context.
"arrow functions do not have their own this
, they inherit it from the outer context."
example 9: declaring an arrow function on the window object (global context).
const callMe = () => {
console.log(this);
}
callMe();
//{window}
This is pretty straight forward.
Now we know that the arrow function will inherit its lexical contexts this.
The context in which callMe() was declared is the global context (window object) so this will refer to it.
example 10: A tricky and long example - declaring a function within an object (object method).
Similar on example 5, what will be logged to the console?
const obj = {
callMethod: () => {
const inner = () => {
console.log(this);
}
return inner();
}
}
obj.callMethod();
Will the this point to obj? I mean we said that arrow function will inherit the reference to this so should it point to the object obj?
NO. that is the tricky part.
Since callMethod()
is an arrow function, it never receives a meaningful this
. We said that arrow function do not have their own this
.
Since there was no this
created for any function above it, it inherited the this
that referenced to the global object.
So what can we do?
There is a rule of thumb: when declaring object method, always create "regular" function expression or function statements.
Here is an example (we are still in example 10):
const obj = {
callMethod: function () { //this is not an arrow function. It will create a 'this' reference to 'obj'
const inner = () => {
console.log(this);
}
return inner();
}
}
obj.callMethod();
Notice the subtle change. callMethod()
was declared as a "regular" function.
Now the result of obj.callMethod? {callMethod: ƒ}
.
example 11: Similar to example 7. What will this refer to?
const outer = () => {
function inner() {
let innerObject = {
method: () => {
console.log(this);
}
}
return innerObject.method();
}
return inner();
}
outer();
Notice how functions are declared in the example.
Look for arrow functions (that inherits their this) and for non-arrow functions that either get thier this by the caller or default to a value.
The result: window
object.
Why?
Notice innerObject
method declaration. It is an arrow function. Since no this
was set above it, it will inherit the window
reference.
How can you make it show innerObject
as this
?
Declare the object method using a non-arrow function.
example 12: same as example 11, but under strict mode
What will this refer to?
"use strict"; //the only change
const outer = () => {
//.....
//same code
//....
}
outer();
undefined.
We said that under strict mode
, if not set by the call, this will not default to the window
object but to undefined.
To conclude ES6 and beyond
Arrow functions inherits their this
reference from the outer context.
In many situations, both ES5 and ES6 behavior regarding this
is similar.
When you want to create a meaningful this
, use regular functions, not arrow functions (mainly when creating objects methods.)
What is next
The above explanation of this in jacascript function may provide a solid basis to understanding the topic.
I recommend further reading of the following topics:
- bind(), call() and apply().
- The use of
this
in a constructor. - this in events and events callbacks.