this
is a keyword whose value changes depending on how a function gets called. This is know as the context
.
Let's see six
different cases where this
can take new values:
this
in global contextthis
in object constructionthis
in an object methodthis
in a simple functionthis
in an arrow functionthis
in an event listener
In a global context, this defaults to the window
object in the browser e.g. this
is called outside of any function.
Here, sayName()
is called as a standalone function (not as a method of an object), so this
refers to the global object (window
).
function sayName() {
console.log(this.name);
}
const name = 'Global';
sayName(); // 'Global'
When we create a new instance of an object with the new
keyword, this
refers to the instance.
when a function is used as a constructor with the "new" keyword, a new object is created and the "this" keyword inside the function refers to the newly created object, which is returned implicitly by the constructor function.
function Human(age) {
this.age = age;
}
const alice = new Human(10);
const bob = new Human(20);
console.log(alice); // this.age = 10
console.log(bob); // this.age = 20
When this
keyword inside an object method refers to the object that the method is a property of.
const ob = {
name: 'Sajib',
sayThis: function () {
console.log(this);
},
};
ob.sayThis(); // Sajib
However, the value of this
can change if the method is invoked using a different context or, if the method is assigned to a variable and invoked as a standalone function.
let ob = {
name: 'Sajib',
sayName: function () {
console.log(this.name);
},
};
let anotherOb = { name: 'Frontend' };
let sayName = ob.sayName;
sayName(); // undefined, this = window
sayName.call(anotherOb); // 'Frontend', this = anotherOb
Inside simple functions this
refers to Window
. Simple functions are functions we know extremely well.
Example 1:
function simpleFunc() {
console.log(this); // Window
}
const ob = {
sayThis() {
simpleFunc();
},
};
simpleFunc(); // Window
ob.sayThis(); // Window
Anonymous functions written in the same form are also considered as simple functions.
Example 2:
const o = {
speakLeet() {
console.log('Hello World!');
},
doSomethingLater() {
setTimeout(function () {
this.speakLeet(); // error
}, 1000);
},
};
Unfortunately, the code above results in an error. The error occurs because this
is set to Window
in the setTimeout
methods. Window
does not have a speakLeet
method.
Exception: In strict mode, rules are different. Context remains as whatever it was set to.
Example 3:
function Counter() {
this.num = 0;
this.timer = setInterval(function add() {
this.num++;
console.log(this.num);
}, 1000);
}
var b = new Counter();
// NaN
// NaN
// NaN
// ...
Our setInterval
function is not called on a declared object. It also isn't being called with the new
keyword (only the Counter()
function is) And lastly, we are not using call
, bind
, or apply
. Here, setInterval is just a normal function() so, the value of this
is being bound to global object!
N.B. The arrow function does not create its own this
, the this
value of the enclosing execution context is used. Thus, in the following code, the this
within the function that is passed to setInterval
has the same values as this
in the enclosing function.
We can solve the issue using Arrow function.
Example 4:
function Counter() {
this.num = 0;
this.timer = setInterval(() => {
this.num++;
console.log(this.num);
}, 1000);
}
var b = new Counter();
// 1
// 2
// 3
// ...
The original this
binding created by the Counter
constructor function is preserved. Inside the setInterval
function, this
is still bound to our newly created b
object!
Example 5:
function f2() {
'use strict';
return this;
}
console.log(f2() === window); // False
console.log(f2() === undefined); // True
this
was not defined, so it's remained undefined
.
In a arrow functions this
is always the same as this
around it (in its immediate scope, also called lexical scope).
So, the this
context stays as the object, not Window
.
With arrow functions, the speakLeet
example above could be written:
const ob = {
doSomethingLater: function () {
setTimeout(() => this.speakLeet(), 1000);
}
speakLeet: function () {
console.log("Method of 'ob' not Window");
}
}
ob.doSomethingLater(); // Method of 'ob' not Window
N.B: We can also set this
inside a method using bind
, call
, apply
.
var bar = 'bar of global';
var foo = {
bar: 'bar of foo';
};
function f2() {
return this.bar;
}
console.log(f2()); // bar of global
console.log(f2.call(foo)); // bar of foo
console.log(f2.apply()); // bar of foo
var bound = f2.bind(document)
var bound2 = f2.bind({ a: 15 });
console.log(bound()); // undefined, there is no variable in document object
console.log(bound.call(window)); // undefined, no variable in document object. In this situation, call can't change the context
console.log(bound2()); // 15, created a new object { a: 15 } and called f2() in this context
Exceptional: Since this
is not bound in arrow functions, the methods call()
or apply()
can only pass in parameters. this
is ignored.
var adder = {
base: 1,
add: function (a) {
var f = (v) => v + this.base;
return f(a);
},
addThroughCall: function (a) {
var f = (v) => v + this.base;
var b = {
base: 2,
};
return f.call(b, a);
},
};
console.log(adder.add(1)); // output: 2
console.log(adder.addThruCall(1)); // output: 2
When the event listener fires, this
is set to the HTML element that fired the event.
let button = document.querySelector('button');
button.addEventListener('click', function () {
console.log(this); // button
});
- if we are in
Strict
mode thenthis = undefined
otherwisethis = global object
function foo() {
console.log(this.bar);
}
var bar = 'bar1';
var o2 = { bar: 'bar2', foo: foo };
var o3 = { bar: 'bar3', foo: foo };
foo(); // bar1
o2.foo(); // bar2
o3.foo(); // bar3
function foo() {
console.log(this.bar);
}
var o1 = { bar: 'bar' };
var o2 = { bar: 'bar2' };
var foo2 = foo;
foo3 = function () {
foo2.call(o1);
};
// foo = function(){ foo2.call(o1); };
foo3(); // bar
foo3.call(o2); // bar !! cause it is hard binding to o1
// foo();
// foo.call(o2); // bar !! cause it is hard binding to o1
- Using
custom bind
utility function
function bind(fn, o) {
return function () {
fn.call(o);
};
}
function foo() {
console.log(this.bar);
}
var o1 = { bar: 'bar' };
var o2 = { bar: 'bar2' };
var foo2 = foo;
foo = bind(foo, o1);
foo3(); // bar
foo3.call(o2); // bar !! cause it is hard binding to o1
- Was the function called with
new
? - Was the function called with
call
orapply
specifying an explicitthis
? - Was the function called via a containing/owning object (context)?
- DEFAULT: global object (except strict mode)