3

I am using lodash to debounce a function call but I am wondering why my this value is not inheriting the scope like I expect.

These are are the relevant parts of my Vue component.

import debounce from 'lodash/debounce';

watch: {
    query: debounce(() => {
        this.autocomplete();
    }, 200, {
        leading: false,
        trailing: true
    }),

The above case does not work because my this value does not point to the Vue component but rather shows an Object like this:

Object
    __esModule: true
    default: Object
    __proto: Object

Isn't my arrow syntax suppose to inherit the context of this?

The following seem to works fine:

query: debounce(function test() {
    this.autocomplete();
}, 200, {
    leading: false,
    trailing: true
})

There is probably an easy answer for this but I am hoping someone can help me out here.

Stephan-v
  • 19,255
  • 31
  • 115
  • 201
  • 1
    Use regular functions instead of arrow functions if you need Vue `this` context. You can find it throughout Vue's documentation. – Egor Stambakio May 31 '17 at 08:31
  • You should probably read up on how arrow functions work. E.g. http://exploringjs.com/es6/ch_arrow-functions.html – nils May 31 '17 at 08:37
  • @nils They work exactly as I expect them to work, just not with Vue apparently. The answer below explains more. – Stephan-v May 31 '17 at 08:38
  • Yes, that is what I was trying to point to. It's easy to make wrong assumptions about the current scope of arrow functions (this has nothing to do with vue). – nils May 31 '17 at 08:41
  • @nils Care to explain why the parent context is not what I expect it to be than? There are probably a lot of people who fall for this, since they have taken it into the official documentation. Might help some people. – Stephan-v May 31 '17 at 08:43
  • What about this though? https://forum-archive.vuejs.org/topic/3792/changing-to-es2015-using-arrows-i-got-an-error-for-this/5 It seems because Vue binds these properties, methods and events by default that is why the context is different. – Stephan-v May 31 '17 at 08:50
  • 1
    I'll post a separate answer about it. – nils May 31 '17 at 08:57

2 Answers2

7

See https://v2.vuejs.org/v2/guide/instance.html#Properties-and-Methods

Don’t use arrow functions on an instance property or callback (e.g. vm.$watch('a', newVal => this.myMethod())). As arrow functions are bound to the parent context, this will not be the Vue instance as you’d expect and this.myMethod will be undefined.

As the same limitation applies to watchers, you have to use something like this:

watch: {
    query: function() {
        return debounce(() => {
            this.autocomplete();
        },
        200,
        {
            leading: false,
            trailing: true
        });
   }
}
tony19
  • 125,647
  • 18
  • 229
  • 307
str
  • 42,689
  • 17
  • 109
  • 127
7

This is only an additional answer to explain the misunderstanding of this in arrow functions.

How does this work in arrow functions?

this in lexical functions always refers to the surrounding scope. That can either be:

  1. The nearest surrounding function
  2. The nearest surrounding module
  3. The global scope

If we have a look at your code, and we assume you're using ES6 modules (judging from the import/export statements):

import debounce from 'lodash/debounce';

export default {
    watch: {
        query: debounce(() => {
            this.autocomplete();
        }, 200, {
            leading: false,
            trailing: true
        }),
    }
};

Let's go through the list:

1. The nearest surrounding function

There is no surrounding function for your arrow function. An example would be:

var obj = {
    a: function() {
        return () => {
            console.log(this);
        }
    }
};

obj.a()(); // `this` refers to `obj`, because `this` refers to `obj` in the surrounding function `a`

2. The nearest surrounding module

Since we are in a (fake) module in this case, this is defined in the module scope as pseudo module object (probably a babel or webpack object?).

Object
    __esModule: true
    default: Object
    __proto: Object

It seems because Vue binds these properties, methods and events by default

That's true and it's a very useful feature of vue. But it doesn't help us in this case, because this cannot be overridden in an arrow function, it always refers to the surrounding scope.

Have a look at the following link for a deeper understanding of arrow functions: http://exploringjs.com/es6/ch_arrow-functions.html#_variables-that-are-lexical-in-arrow-functions

nils
  • 25,734
  • 5
  • 70
  • 79
  • Thanks, but I think str's answer is more on point as it actually solves your problem. I only provide context. – nils May 31 '17 at 09:50