Правильно ли я использую обратные вызовы Javascript (объектно-ориентированный стиль)? - PullRequest
2 голосов
/ 18 января 2011

У меня есть следующий пример кода для использования объекта, который получает действие от обратного вызова.Не похоже, что это хороший шаблон дизайна.Либо это?

Когда setTimeOut() запускает функцию через 1 секунду, она использует глобальную переменную objInstance (область DOM) для доступа к экземпляру объекта ClassExample.Кто-то может порекомендовать лучший способ использовать обратные вызовы в объектно-ориентированном дизайне?

Вся идея в том, чтобы я мог использовать обратный вызов для обновления данных в моем экземпляре объекта (например, увеличить переменную).

function ClassExample{
    this.initiate = function() {
        setTimeOut(objInstance.afterTimeOut,1000); //using the objects global handle
    }

    this.afterTimeOut = function() {
        alert("Received!");
    }

}

var objInstance = new ClassExample(); //instance
objInstance.initiate();

Ответы [ 6 ]

4 голосов
/ 18 января 2011

Нет, это не так. Вы хотите сделать это:

this.initiate = function() {
    setTimeOut(objInstance.afterTimeOut,1000); //using the objects global handle
}

Теперь, если для "afterTimeout" нужен правильный контекст объекта, вы можете сделать это:

this.initiate = function() {
  var instance = this;
  setTimeout(function() { instance.afterTimeOut(); }, 1000);
}

Хорошо, вы значительно изменили вопрос с этим небольшим редактированием :-) На вашем месте, я бы просто сделал это (как мой оригинальный второй пример):

this.initiate = function() {
  var instance = this;
  setTimeout(function() { instance.afterTimeOut(); }, 1000);
}

Тогда вам вообще не нужны ужасные глобальные переменные.

изменить & mdash; Пользователь @Christoph из Stackoverflow отмечает, что это не очень красиво. Одной вещью, которая может помочь, было бы использование средства «связывания», предоставляемого изначально новыми браузерами (как метод в прототипе Function) или некоторыми библиотеками (например, Prototype или Functional). То, что «связывание» позволяет вам сделать, - это создать небольшую функцию-обертку, как я описал выше:

this.initiate = function() {
  setTimeout(this.afterTimeOut.bind(this), 1000);
}

Этот вызов "bind" возвращает функцию, которая по сути является той же вещью, что и маленькая оболочка, которую я явно закодировал в примере.

2 голосов
/ 18 января 2011
function ClassExample{
    this.afterTimeOut = function() {
        alert("Received!");
    }; // Don't forget these

    setTimeOut(afterTimeOut, 1000); // Don't use () if you're passing the function as an argument
}

var objInstance = new ClassExample(); //instance

Таким образом, вам не нужен метод initiate().


Если вы действительно хотите метод initiate(), я бы сделал это так:

function ClassExample{
    var self = this;

    self.afterTimeOut = function() {
        alert("Received!");
    };

    self.initiate = function() {
        setTimeOut(self.afterTimeOut, 1000);
    };

}

var objInstance = new ClassExample(); //instance
objInstance.initiate();
1 голос
/ 18 января 2011

Вот как я могу это сделать, чтобы разрешить повторное использование таймера и минимизировать количество замыканий:

function Timer(timeout, callback) {
    this.timeout = timeout;
    this.callback = callback;
}

Timer.prototype.run = function(thisArg /*, args... */) {
    var argArray = Array.prototype.slice.call(arguments, 1);
    var timer = this;

    setTimeout(function() {
        timer.callback.apply(thisArg, argArray);
    }, timer.timeout);
};

var timer = new Timer(1000, alert);
timer.run(null, 'timer fired!');

И просто для удовольствия, версия для гольфа, которая функционально эквивалентна, но заменяет объект замыканием:

function delay(func, timeout) {
    return function() {
        var self = this, args = arguments;
        setTimeout(function() { func.apply(self, args); }, timeout);
    };
}

delay(alert, 1000).call(null, 'timer fired!');
0 голосов
/ 17 мая 2016

Опираясь на ответ Знакарса ...

Я действительно не знаю, в какой среде работает его код, но для меня первый подход просто не работает.Я получил: ' ReferenceError: afterTimeOut не определен ' ...

Второй, тем не менее, действительно классный ... Я просто изменил setTimeOut для setTimeout (используя строчную букву 'o') и включили скобки после определения имени класса, превращая первую строку кода в 'function ClassExample () {';решил мою проблему.

Мой пример кода:

Упс с приватным поведением, внутренним обратным вызовом и т. д.

function MyTry (name){
   // keep this object pointer... that's the trick!
   var self = this;

   // create private variable
   var d = new Date()toJSON().slice(0, 10);

   // create a private function
   function getName(){return name}

   // create public access method
   self.hello  = function(){alert('Hello '+getName()+'!\nToday is: '+d)}

   // note instance method hello passed as a callback function!
   self.initiate = function(){setTimeout(self.hello, 3000)}
}
0 голосов
/ 18 января 2011
this.initiate = function() {
    var instance = this;
    setTimeOut(function() {
        instance.afterTimeOut();
    }, 1000);
};

Сохраняя this в локальной переменной, вы можете вообще не использовать глобальный дескриптор.Также это предотвращает потерю afterTimeout() this.

0 голосов
/ 18 января 2011

Вы правы, это не оптимальный способ сделать то, к чему вы стремитесь.однако я должен задаться вопросом, почему вам нужно прерывать стек вызовов как часть инициации, это выглядит очень академично.

Помимо этого, если бы мне пришлось это сделать, я бы, вероятно, использовал замыкание следующим образом:1003 *

function ClassExample{
    this.initiate = function() {
        setTimeOut((function(self) { return function() { self.afterTimeout();}})(this),1000); //using the objects global handle
    }

    this.afterTimeOut = function() {
        alert("Received!");
    }

}

var objInstance = new ClassExample(); //instance
objInstance.initiate()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...