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!
Top comments (8)
I'd turn your statement around and say we should avoid
this
in Vue, but keep using arrow functions. Arrow functions are concise and eliminate the complexity of functions scopes.this
can be hard to understand itself.The composition API and it's setup method provide a way to define your reactive data, methods, etc. in one function. Watchers, refs and the likes are imported as functions as you go, eliminating any need for
this
.Interesting. But I still code in Vue v2 and frequently use 'this' with regular functions. What's the point of giving up 'this' in favor of arrow functions, when it still works the way it is?
If it works for you, then great :) I'm not telling you to refactor your entire codebase.
this
is context aware and changes its meaning depending on its scope. That's inherently hard to read and understand.Scope is also a problem that gets simpler with arrow functions, because they inherit their parent scope and don't create their own.
The point in giving up
this
is writing more readable code.Fair point. I find the composition API so radically different from the component based composition that it essentially becomes an architectural choice you'll have to make depending on your needs.
Great post and well explained!
I recently encountered some of these issues when writing methods in Vue and your post clearly and concisely explains the logic behind them. Understanding 'this' is very useful.
Saying that arrow function "binds to the parent scope" is just plain wrong and misleading. If you refer to MDN page, you will immediately see that arrow functions don't bind to ANYTHING ("Does not have its own bindings to this or super, and should not be used as methods") - which also tells you specifically not to use them as methods.
Understanding "this" is very important for writing proper JS, but I don't see the point of this article while knowing the exact same thing is stated in the doc (with less confusing wording).
developer.mozilla.org/en-US/docs/W...
Not wrong or misleading at all. As stated: "The really important takeaway here is that arrow functions do not have their own this."
On MDN
"because Arrow functions establish "this" based on the scope the Arrow function is defined within."
developer.mozilla.org/en-US/docs/W...
I knew there was something strange about arrow functions and "this", but thanks to your excellent post, it's clarified now. Thank you! π