Becky
Becky

Reputation: 348

JavaScript class methods

I have defined my class like so:

function Slot(slot, maxSpeed, timer) {
this.slot = slot;
this.speed = 0;
this.maxSpeed = maxSpeed;
this.timer = timer;
this.interval = null;

this.go = function() {
    var $slot = $(this.slot);
    console.log(this.slot);
    $slot.addClass('motion');
    $slot.spStart();

    this.interval = window.setInterval(function() {
        var step = Math.floor(Math.random() * ((this.speed / 100) * 25)) + 1;
        if (this.speed < (this.maxSpeed / 2)) {
            this.speed += step;
        }

        if (this.speed >= (this.maxSpeed / 2)) {
            this.speed -= step;
        }
        console.log(this.slot);
        $slot.spSpeed(this.speed);
    }, timer);
};

$(this.slot).pan({ fps: '30', dir: 'down' });
$(this.slot).spStop();
}

The first console.log returns the expected value, but once I get into the setInterval function all variables (this.slot, this.speed) are all undefined? Even though I am still within their scope...

Upvotes: 1

Views: 264

Answers (5)

When setInterval() executes your callback method it does so outside your class context, so 'this' does not refer to your class. You must pass on your class's 'this' reference as an argument to your callback.

var that = this;
setInterval(function() {
  that.someVar ...
  that.someMethod() ...
});

Upvotes: 1

SergeS
SergeS

Reputation: 11779

this is simple, put

var self = this;

into this.go, and evrywhere in timer function replace this with self -

this.go = function() {
     var $slot = $(this.slot);
     console.log(this.slot);
     $slot.addClass('motion');
     $slot.spStart();
     var self = this;

     this.interval = window.setInterval(function() {
         var step = Math.floor(Math.random() * ((self.speed / 100) * 25)) + 1;
         if (self.speed < (self.maxSpeed / 2)) {
             self.speed += step;
         }

         if (self.speed >= (self.maxSpeed / 2)) {
             self.speed -= step;
         }
         console.log(self.slot);
         $slot.spSpeed(self.speed);
     }, timer);
 };

`

Upvotes: 1

Paul
Paul

Reputation: 36339

Scoping is a bit weird to get used to in Javascript, even weirder when you start using setInterval and setTimeout.

In your case, the this that's inside the interval is referring to the anonymous function itself. You can either assign 'this' to another variable outside the anonymous function:

var self = this;
this.interval = window.setInterval(function(){ /* use self here instead of this*/}

or you can call a function on the object in the interval step:

this.interval = window.setInterval(this.callback, timer);

Upvotes: 2

pimvdb
pimvdb

Reputation: 154868

No, because this refers to something else inside setInterval. You'd need to define a static copy before like:

this.go = function() {
    var $slot = $(this.slot);
    console.log(this.slot);
    $slot.addClass('motion');
    $slot.spStart();

    var that = this;

    this.interval = window.setInterval(function() {
        var step = Math.floor(Math.random() * ((that.speed / 100) * 25)) + 1;
        if (that.speed < (that.maxSpeed / 2)) {
            that.speed += step;
        }

        if (that.speed >= (that.maxSpeed / 2)) {
            that.speed -= step;
        }
        console.log(that.slot);
        $slot.spSpeed(that.speed);
    }, timer);
};

Upvotes: 1

Matt
Matt

Reputation: 41832

Your statement "Even though I am still within their scope" is incorrect. The 'this' variable is always set the functional context in which a function is executed, not in which it is defined. In this case, the function inside of setInterval is being executed from the window scope, and 'this' is actually window.

To get around this problem, I recommend using a closure: (notice the addition of the 'self' variable, and replacing nested 'this' calls with 'self'

function Slot(slot, maxSpeed, timer) {
    var self = this;
    this.slot = slot;
    this.speed = 0;
    this.maxSpeed = maxSpeed;
    this.timer = timer;
    this.interval = null;

    this.go = function() {
        var $slot = $(self.slot);
        console.log(self.slot);
        $slot.addClass('motion');
        $slot.spStart();

        self.interval = window.setInterval(function() {
            var step = Math.floor(Math.random() * ((self.speed / 100) * 25)) + 1;
            if (self.speed < (self.maxSpeed / 2)) {
                self.speed += step;
            }

            if (self.speed >= self.maxSpeed / 2)) {
                self.speed -= step;
            }
            console.log(self.slot);
            $slot.spSpeed(self.speed);
        }, timer);
    };

    $(self.slot).pan({ fps: '30', dir: 'down' });
    $(self.slot).spStop();
}

Upvotes: 1

Related Questions