What is "this"? Why you should avoid arrow functions on Vue methods
JS Bits Bill
Posted on April 23, 2021
this
in Vue
Every Vue instance has an option for methods. This is simply an object whose properties are methods we'll use in our Vue app:
const app = Vue.createApp({
data() {
return { count: 4 };
},
methods: {
increment() {
// "this" will refer to the component instance
this.count++;
}
}
});
Vue will bind the this
keyword to the instance so that it will always reference the component instance. Because of this, it's really import to not use arrow functions when defining methods because they always bind this to the parent context, which is not actually the Vue instance - but the global object (the Window):
const app = Vue.createApp({
data() {
return { count: 4 };
},
methods: {
increment: () => {
// "this" will refer to the Window
this.count++;
}
}
});
Y Tho
The reason is that every regular (non-arrow) function defines its own this
value, which always refers to the owner of the function it's in.
So in this example:
const person = {
name: 'Ted',
logName() {
console.log(this.name); // Ted
console.log(this); // person object
}
};
person.logName();
this
refers to the person
object, which is logName
's owner.
This is true even when inside a stand alone function:
function test() { console.log(this); }
test(); // Window is logged
That's because the owner of test
is the window object:
window.test; // test() { console.log('this', this); }
There's a huge exception to this. Whenever this
is used inside of a function within another method, its binding is lost and this
will then refer to the global (window) object:
const obj = {
func1() {
console.log('func1 this', this); // "this" is obj
(function func2() {
// "this" binding is lost here
console.log('func2 this', this); // "this" is Window
})();
}
};
obj.func1();
This is considered somewhat of a bug in the JavaScript language since it's very quirky and trips up a lot of people.
When arrow functions were released in ES6 they provided a way to force this
to automatically bind to the parent scope which produces a more expected outcome:
const obj = {
func1() {
console.log('func1 this', this); // "this" is obj
(() => {
console.log('func2 this', this); // "this" is obj
// "this" was bound to func1's "this" reference
})();
}
};
obj.func1();
The really important takeaway here is that arrow functions do not have their own this
. When you use the this
keyword inside an arrow function you're referring to the this
of either a surrounding regular function/method or the global object if there is none.
Let's look at another example:
const person = {
firstName: 'Bob',
getName() {
console.log(this.firstName);
}
};
person.getName();// Bob
person.getName
is a regular old function. That means it has its own this
reference - which we learned is the owner of the function - the person
object.
So what happens when we make getName
an arrow function?
const person = {
firstName: 'Bob',
getName: () => {
console.log(this.firstName);
}
};
person.getName(); // undefined
this.firstName
is undefined
in this case. Why? Because the getName
arrow function is binding the this
keyword to the this
of a surrounding regular function, which there is none - so the global object is what's bound to this
. And window.firstName
is of course undefined
.
Tying it back to Vue
With this in mind, let's look back at a Vue instance object:
const app = Vue.createApp({
data() {
return {
firstName: 'Bob'
}
},
methods: {
getName() {
console.log(this.firstName); // Bob
}
},
created() {
this.getName();
}
});
this
is being used inside a regular function and not arrow functions which means this
is bound to an owner object. If we were to make getName
an arrow function it would mean this
becomes the global object like we saw in our previous examples.
It's important to note that when using regular functions, Vue does its own assignment of the this
keyword to be the actual Vue instance - so the owner object is a little different than if we were using our own custom object. This under-the-hood mapping allows us to access data properties and methods like this.otherMethod
and this.lastName
which is convenient.
One last thing
While you should not use arrow functions to define methods, it's fine to use them inside your methods as the this
keyword will bind to the correct parent reference.
const app = Vue.createApp({
data() {
return {
checkmark: '✔',
letters: ['a', 'b', 'c']
}
},
methods: {
processLetters() {
// Using arrow functions inside processLetters is fine!
const processedArray = this.letters.map(letter => {
// "this" here binds to the "this" of processLetters
return `${letter}-${this.checkmark}`
});
console.log(processedArray); // ["a-✔", "b-✔", "c-✔"]
}
},
created() {
this.processLetters();
}
});
Check out more #JSBits at my blog, jsbits-yo.com. Or follow me on Twitter!
Posted on April 23, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
September 4, 2024
August 17, 2024
July 6, 2024