Cannot use 'this' keyword in an object because it is inside another function

Viewed 457

I am using vue.js in this case but I guess it would apply in plain JS too. The problem is that when I am in a function that is in another function I am having to call variables by their full path like - Object.variable instead of this.variable. Is there a way to use this.timer, this.pages instead of TVComponent.pages etc.

const TVComponent = new Vue ({
el: '.tvContent',

data:
{
    current_page: 0,
    timer: 0,
    pages: [
      { page: '/', interval: 10 },
      { page: 'tv/calls', interval: 10 },
      { page: 'tv/general', interval: 10 }
    ]
},

methods:
{
  tvTimer()
  {
     setInterval(function() {
        TVComponent.timer++;

        if (TVComponent.timer == TVComponent.pages[TVComponent.current_page].interval) {
           console.log('it is time!!');
        }

        console.log(TVComponent.pages[TVComponent.current_page].page);
     }, 1000);
  },
})
4 Answers

Classic problem of this being not what you expect in a function

A. bind it

methods:
{
  tvTimer()
  {
     setInterval(function() {
        // ...
     }.bind(this), 1000);
  },
})

B. use a closure

methods:
{
  tvTimer()
  const _this  = this
  {
     setInterval(function() {
        _this.timer ...
     }, 1000);
  },
})

C. use an arrow function

methods:
{
  tvTimer()
  {
     setInterval(() => {
        this.timer ...
     }, 1000);
  },
})

This is one of those things one has to really understand about JS in order to not fall for it over and over in different places. I suggest this ebook:

https://github.com/getify/You-Dont-Know-JS/blob/master/up%20%26%20going/ch2.md#this-identifier

The function you pass to setInterval receives its own context, however if you use an arrow function it will use the current context instead, and ignore the one given by setInterval.

setInterval(() => { ... }, 1000)

This is because setInterval() this object is not same as vue.js this, since they have different scopes.

Try to assign this object to new variable before entering the problematic function's scope.

let self = this;
tvTimer()
  {
     setInterval(function() {
        self.timer++;

        if (self.timer == self.pages[self.current_page].interval) {
           console.log('it is time!!');
        }

        console.log(self.pages[self.current_page].page);
     }, 1000);
  },

See: https://developer.mozilla.org/en-US/docs/Glossary/Scope

I think you have to bind it in this context. We do it this way in React classess.

Related