Maybe the biggest advantage of using arrow function is that they share the surrounding this keyword, this means unlike normal functions, arrow functions don’t get their own ‘this’ keyword.
Again,
Arrow functions don’t have ‘this’ keyword, they simply use the ‘this’ keyword of the function they are written in.
So we say, they have a lexical ‘this’ variable.
ES5
var box5 = {
color: 'green',
position: 1,
clickMe: function() {
//attach an event handler, we want it to happen on the click
document.querySelector('.green').addEventListener('click',
function() {
var str = 'This is box number' + this.position + ' and it is
' + this.color;
alert(str);
});
}
}
box5.clickMe();
//so as soon as this method is called, the event listener will be added to the element.
What will happen if we click on the green box?
This is box number undefined and it is undefined.
Why it’s not reading the value from our object?
This is because only the method calls, the this keyword is actually pointing to the object. But in a regular function call, the this keyword will always point to the global object which in the case of browser is the window object.
This is exactly what happened here. The clickMe method is a function attached to an object, so it’s a method, and so in here we have access to the position and color using the this keyword, but the callback function we have in the event handler is not a method, is a regular function call, therefore, ‘this’ keyword here does not point to box5 object, instead, it points to the window object.
And of course, the position and color are not defined on the window object. Thus, we have undefined on both.
A common pattern to avoid this is to simply create a variable.
var box5 = {
color: 'green',
position: 1,
clickMe: function() {
//attach an event handler, we want it to happen on the click
var self = this;
document.querySelector('.green').addEventListener('click',
function() {
var str = 'This is box number ' + self.position + ' and it
is ' + self.color;
alert(str);
});
}
}
box5.clickMe();
//This is box number 1 and it is green
So the self variable points to this. (We basically store this variable in the self variable hence we can use it in the rest of the function)
This is kind of a hack to avoid this situation as in the function call here we don’t have access to the box5 object.
Ok. Now move on the ES6. Remeber we say the arrow function share surronding this keyword? Let’s see how we can leverage on it to avoid this hack here.
ES6
const box5 = {
color: 'green',
position: 1,
clickMe: function() {
//attach an event handler, we want it to happen on the click
document.querySelector('.green').addEventListener('click', ()={
var str = 'This is box number ' + this.position + ' and it is ' + this.color;
alert(str);
});
}
}
box5.clickMe();
//This is box number 1 and it is green
If we don’t have any argument or if we have more than one argument, we use () and then add the function body.
Ok, so now this arrow function shares the this keyword with its surrounding which is clickMe method, and we know in here the this keyword points to the object, hence in the function we can use ‘this’ keyword.
Thus, the best practice is always using arrow functions when you need to preserve the value of the ‘this’ keyword.
Another scenario is
const box66 = {
color: 'green',
position: 1,
clickMe: () => {
//attach an event handler, we want it to happen on the click
document.querySelector('.green').addEventListener('click', ()
=> {
var str = 'This is box number ' + this.position + ' and it
is ' + this.color;
alert(str);
});
}
}
box66.clickMe();
Will we obtain the same result?
=> This is box number undefined and it is undefined
Why is that?
This is because this method here now also shares the lexical ‘this’ keyword from its surrounding. And the surrounding of it is the global context, which means this method here also no longer has its own ‘this’ keyword, which means it shares the global ‘this’ keyword, which of course points to the global object window. And now we face the same situation where we don’t have color and position defined on the window, so it become undefined.
Thus, be careful with the arrow function so we won’t lose track of what the ‘this’ keyword actually points to.
Another example
Let’s create a function constructor to create a person object.
function Person(name) {
this.name = name;
}
Add a method to the prototype propery of the Person, so the object created through the object constructor will inherite this method. (it recieves an array of friends)
ES5
function Person(name) {
this.name = name;
}
Person.prototype.myFriends5 = function(friends) {
var arr = friends.map(function(el) {
return this.name + ' is friends with ' + el;
});
console.log(arr);
}
var friends = ['Bob', 'Hank', 'Peter'];
new Person('John').myFriends5(friends);
// new and call the method immediately and pass friends into it.
Will this work?
(3) [" is friends with Bob", " is friends with Hank", " is friends with Peter"]
Name is undefined. Due to the exact same reason as before.
In this method, we of course have access to the this variable and it points to the name of the person which will be John. But the thing is in here we call another function which is the anonymous function.
function(el) {
return this.name + ' is friends with ' + el;
}
Hence in here the this keyword is not going to point to the john object but instead, it points to the global object, which is window once again. So we can use that trick again. (var self = this, basically storing the this variable in a separate variable)
But let’s use another trick.
Remember call, bind, apply allow us to define ‘this’ keyword manually and bind, create a copy of this function while actually calls it.
Thus what we can do here is to create a copy of this function with the ‘this’ variable set to this.
function(el) {
return this.name + ' is friends with ' + el;
}
This is the function, so in this function we can create a copy of the function using bind method. We want ‘this’ keyword to be this, remember, outside the anonymous function we still have access to the this variable and it points to the new Person which is John in this case, so we pass it to the function simply be creating a new copy of a function with a manually defined this keyword.
function Person(name) {
this.name = name;
}
Person.prototype.myFriends5 = function(friends) {
var arr = friends.map(function(el) {
return this.name + ' is friends with ' + el;
}.bind(this));
console.log(arr);
}
var friends = ['Bob', 'Hank', 'Peter'];
new Person('John').myFriends5(friends);
// (3) ["John is friends with Bob", "John is friends with Hank", "John is friends with Peter"]
In a regular function call, the ‘this’ keyword will always point to the global object(window) and when we have several functions or methods, the ‘this’ keyword gets lost. So to get around this we normally create a variable within the function block called ‘self’ that stores the ‘this’ keyword, and instead of using ‘this.position’ we use ‘self.position’.
However there is another way, and that is to bind the ‘this’ keyword to a specific function by using the bind() method which sets the value of “this” in the target function when the bound function is called.
ES6 version
Person.prototype.myFriends6 = function(friends) {
var arr = friends.map(el => `${this.name} is friends with ${el}.`);
console.log(arr);
}
As mentioned before, this function does not have its own ‘this’ keyword, which means it shares the lexical ‘this’ keyword from its surrounding which in this case is this methods where ‘this’ points to the instance.
new Person('Mike').myFriends6(friends);
// (3) ["Mike is friends with Bob.", "Mike is friends with Hank.", "Mike is friends with Peter."]