简体   繁体   中英

How to call a function from `setInterval`?

function Obj() {
    this.func1 = function() {
            this.func2();
        },
        this.func2 = function() {
            setInterval(function() {
                this.func2();
            }, 200);
        }
}

function main() {
    var obj = new Obj();
    obj.func1();
}
main();

I have the following exception:

this.func2(); ^ TypeError: this.func2 is not a function

at Timeout._onTimeout(C: \Users\ Admin\ Desktop\ CANVAS\ test.js: 15: 12)
at ontimeout(timers.js: 365: 14)
at tryOnTimeout(timers.js: 237: 5)
at Timer.listOnTimeout(timers.js: 207: 5)

Why this.func2 is function when I call without setInterval and is not a function when I call from setInterval ?

Cause this is not referring to Obj in your case. That context has been changed and this is referring to setInterval context.

NOte the line var self = this; , which storing this for later usage.

This should work

function Obj() {



  this.func1 = function() {
    this.func2();

  },

  this.func2 = function() {

    setInterval(function(){
      self.func2();
    }, 200);

  }

}

function main() {
  var obj = new Obj();
  obj.func1();

}

main();

Maybe you wanted to do something like this:

function Obj() {

  this.func1 = function() {
    this.func2();

  },

  this.func2 = function() {

    setInterval(function(){
      this.func2();
    }.bind(this), 200);

  }

}

But this will soon lead to an overflow, because you create a new interval on each execution. Maybe you wanted to use setTimeout ?

You can bind this

this.func2 = function() {
  setInterval(function() {
    this.func2();
  }.bind(this), 200);

}

DEMO

The problem is the context inside of setTimeout is the top-level, not the context of your object, so this will point to window (in a browser) instead of your object. There are a couple of ways to solve the problem.

The most recent version is to use the bind method to bind the context of your function.

 function Obj() { this.func1 = function() { this.func2(); }, this.func2 = function() { console.log('hi'); setTimeout(this.func2.bind(this), 1000); } } function main() { var obj = new Obj(); obj.func1(); } main(); 

Notice that I use this.func2.bind(this) as the callback for setTimeout (same for setInterval , just your example would be quite spammy if I left it with that). That means that no matter where it's called, it's this will always be your object.

The slightly older way to do it is to wrap it up in self-calling function that'll isolate it to a scope that has this set to your object.

 function Obj() { this.func1 = function() { this.func2(); }, this.func2 = function() { console.log('hi'); setTimeout((function(self){ return function () { self.func2(); } })(this), 1000); } } function main() { var obj = new Obj(); obj.func1(); } main(); 

This way is a bit more involved, but basically I just wrapped what you originally had in a self-calling function that passes in this as a parameter for self , then I use self.func2() inside of it.

this is not passed to your setInterval callback because it is always called in the global context, just like when you use setTimeout .

this is either undefined (in 'use strict' mode) or the window object (in regular/loose mode). You can pass extra arguments to setInterval , though, and these are passed on to your callback:

this.func2 = function() {
  setInterval(function(self) {
    self.func2()
  }, 200, this)
}

Demo Snippet

 function Obj() { this.func1 = function() { this.func2() } this.func2 = function() { setInterval(function(self) { console.log(typeof self.func2) //=> 'function' }, 200, this) } } function main() { var obj = new Obj() obj.func1() } main() 

Use arrow function:

  this.func2 = function() { setInterval(() => { this.func2(); }, 200); } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM