DEV Community

Cover image for What is "this"? Why you should avoid arrow functions on Vue methods
JS Bits Bill
JS Bits Bill

Posted on • Edited on

What is "this"? Why you should avoid arrow functions on Vue methods

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++;
    }
  }
});
Enter fullscreen mode Exit fullscreen mode

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++;
    }
  }
});
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

That's because the owner of test is the window object:

window.test; // test() { console.log('this', this); }
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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();
  }
});
Enter fullscreen mode Exit fullscreen mode

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();
  }
});
Enter fullscreen mode Exit fullscreen mode

Check out more #JSBits at my blog, jsbits-yo.com. Or follow me on Twitter!

Top comments (8)

Collapse
 
iamschulz profile image
Daniel Schulz

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.

Collapse
 
christiankozalla profile image
Christian Kozalla

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?

Collapse
 
iamschulz profile image
Daniel Schulz

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.

Collapse
 
js_bits_bill profile image
JS Bits Bill • Edited

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.

Collapse
 
nonimpressed profile image
Richard

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.

Collapse
 
ivanpepelko profile image
Ivan Pepelko

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...

Collapse
 
js_bits_bill profile image
JS Bits Bill

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...

Collapse
 
christiankozalla profile image
Christian Kozalla

I knew there was something strange about arrow functions and "this", but thanks to your excellent post, it's clarified now. Thank you! 👍