In the Real World
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.
1. Callbacks Without Auto Binding
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
.
2. Callbacks With Auto Binding
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.
3. HTML Inline (Auto Binding)
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'
4. Closure
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.
References:
- 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/