Методы класса JavaScript

Я определил свой класс так:

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();
}

Первый console.log возвращает ожидаемое значение, но как только я попаду в функцию setInterval, все переменные (this.slot, this.speed) станут неопределенными? Хотя я все еще в их поле зрения...


person Becky    schedule 09.06.2011    source источник


Ответы (5)


Область действия немного странная, чтобы к ней привыкнуть в Javascript, еще более странная, когда вы начинаете использовать setInterval и setTimeout.

В вашем случае this внутри интервала относится к самой анонимной функции. Вы можете либо присвоить this другой переменной вне анонимной функции:

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

или вы можете вызвать функцию для объекта на шаге интервала:

this.interval = window.setInterval(this.callback, timer);
person Paul    schedule 09.06.2011

Ваше утверждение «Несмотря на то, что я все еще в их сфере действия» неверно. Переменная this всегда задает функциональный контекст, в котором функция выполняется, а не контекст, в котором она определена. В этом случае функция внутри setInterval выполняется из области действия окна, и «это» на самом деле является окном.

Чтобы обойти эту проблему, я рекомендую использовать замыкание: (обратите внимание на добавление переменной 'self' и замену вложенных вызовов 'this' на '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();
}
person Matt    schedule 09.06.2011

Нет, потому что this относится к чему-то еще внутри setInterval. Вам нужно будет определить статическую копию, прежде чем:

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);
};
person pimvdb    schedule 09.06.2011

это просто, поставь

var self = this;

в this.go, и везде в функции таймера замените это на себя -

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);
 };

`

person SergeS    schedule 09.06.2011

Когда setInterval() выполняет ваш метод обратного вызова, он делает это вне контекста вашего класса, поэтому «это» не относится к вашему классу. Вы должны передать ссылку «this» вашего класса в качестве аргумента для вашего обратного вызова.

var that = this;
setInterval(function() {
  that.someVar ...
  that.someMethod() ...
});
person Karl-Bjørnar Øie    schedule 09.06.2011