Make sure you have read last chapter this - basics before you read following.
One reason Javascript looks complicated is: Javascript are usually used to deal with DOM, Async calls and libs written by random people... Things get complicated, in real world, in real interviews.
setTimeout()
is a popular one in interviews:
const tom = {
examAnswer: 'x = 100',
startExam() {
setTimeout(function() {
console.log(`My answer is: ${this.examAnswer}`);
}, 1000);
}
}
tom.startExam();
Answer is code will not work as expected since this
is window
inside the callback.forEach()
has the same issue:
const daenerys = {
home: "King's landing",
dragons: [
{name: 'Drogon', location: 'The Wall'},
{name: 'Rhaegal', location: 'Castel Black'},
{name: 'Viserion', location: 'Winterfell'}
],
// trying to set dragon's location to daenerys.home
dragonsGoHome() {
// attention: callback in forEach()
this.dragons.forEach(function(dragon) {
dragon.location = this.home; // this === window
});
}
}
daenerys.dragonAttack();
daenerys.dragons[0].home; // undefined
The this will become window
again in the callbacks. Why? because in the above setTimeout()
or forEach()
, callback is invoked in purely function form. Kind of like:
// a function supports callback
function deleteDomNodesWithCallback(parentNode, callback) {
// do the deleting stuff
...
// callback is invoked as a purely function
callback();
}
deleteDomNodesWithCallback(iAmTheNode, iAmCallback);
Since we know what just happened, how to fix this
by specifying the right this
?
// Way 1:
// using 'that', or closure
const tom = {
examAnswer: 'x = 100',
startExam() {
var that = this;
setTimeout(function() {
console.log(`My answer is: ${that.examAnswer}`);
}, 1000);
}
}
tom.startExam();
// Way 2:
// use bind(), a cleaner & better way
const tom = {
examAnswer: 'x = 100',
startExam() {
setTimeout(function() {
console.log(`My answer is: ${that.examAnswer}`);
}.bind(this), 1000);
}
}
tom.startExam();
And at last a fix for Daenerys:
var daenerys = {
...
dragonsGoHome() {
var _that = this; // created only for demo purpose
this.dragons.forEach(function(dragon) {
// inside the callback
dragon.location = this.home;
}.bind(this)); // `this` equals to _that
}
...
// daenerys [dot] dragonsGoHome
daenerys.dragonsGoHome();
Since this part once confused me for a long time, please allow me to assume that you need a final explanation:
- When we call
daenerys.dragonsGoHome()
, we passdaenerys
into methoddragonsGoHome()
asthis
. - So inside
dragonsGoHome
,this
equals todaenerys
object. - The
bind(this)
is inside the same context/this-scope asdragonsGoHome()
. Sothis
equalsdaenerys
.
Some libary such as jQuery, or DOM API binds this
for you in the background. Unlike above.
// jQuery.js
$('button').click(function(event) {
console.log(this);
});
// the button object will be logged
// Pure DOM API
document.querySelector('html').addEventListener('click', function() {
console.log(this);
});
// the html DOM object will be logged
Keep in mind those functions' behavior.
Not a recommended practise since we always want to seperate Representing (HTML) and Logic (Javascript).
<button onclick="alert(this.tagName.toLowerCase());">
Show this
</button>
// 'button'
Looks something new at first glance but exactly what we have seen before:
var rhaegal = {
name: 'Rhaegal',
layEgg() {
// hidden private variable in closure
let eggs = 0;
const lay = function() {
eggs++;
console.log(`${this.name}'s eggs are: ${eggs}.`)
}
return lay();
}
}
rhaegal.layEgg();
Answer is: 's eggs are 1.
Remember: How a function is invoked is the most important thing. The function lay()
is invoked as purely function
thus the this
inside will be window
. Simply like that.
How to fix? Similarly to the above example tom
. We can either use that
or bind
. Or arrow function =>
:
var rhaegal = {
name: 'Rhaegal',
layEgg() {
// hidden private variable in closure
let eggs = 0;
const lay = () => {
eggs++;
console.log(`${this.name}'s eggs are: ${eggs}.`)
}
return lay();
}
}
rhaegal.layEgg(); // Rhaegal's eggs are: 1.
- Understanding Javascript's this With Clarity, and Master It: http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/
- this (MDN) https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
- The Final Steps to Mastering JavaScript’s “this” Keyword https://www.sitepoint.com/mastering-javascripts-this-keyword/