Область Обратного Вызова JavaScript


у меня возникли некоторые проблемы с обычным старым JavaScript (без фреймворков) при ссылке на мой объект в функции обратного вызова.

function foo(id) {
    this.dom = document.getElementById(id);
    this.bar = 5;
    var self = this;
    this.dom.addEventListener("click", self.onclick, false);
}

foo.prototype = {
    onclick : function() {
        this.bar = 7;
    }
};

теперь, когда я создаю новый объект (после загрузки DOM, с тестом span#)

var x = new foo('test');

"это" внутри функции onclick указывает на тест span#, а не на объект foo.

Как мне получить ссылку на мой объект foo внутри функции onclick?

7 58

7 ответов:

(извлечено некоторое объяснение, которое было скрыто в комментариях в другом ответе)

проблема заключается в следующей строке:

this.dom.addEventListener("click", self.onclick, false);

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

проблема может быть решена путем обертывания функции (с ее ссылкой на объект) в замыкании как следует:

this.dom.addEventListener(
  "click",
  function(event) {self.onclick(event)},
  false);

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

альтернативный способ решить эту проблему-сделать функцию полезности (и избегать использования переменных для привязки этой):

function bind(scope, fn) {
    return function () {
        fn.apply(scope, arguments);
    };
}

обновленный код будет выглядеть например:

this.dom.addEventListener("click", bind(this, this.onclick), false);

Function.prototype.bind является частью ECMAScript 5 и обеспечивает ту же функциональность. Так что вы можете сделать:

this.dom.addEventListener("click", this.onclick.bind(this), false);

для браузеров, которые еще не поддерживают ES5,MDN предоставляет следующую прокладку:

if (!Function.prototype.bind) {  
  Function.prototype.bind = function (oThis) {  
    if (typeof this !== "function") {  
      // closest thing possible to the ECMAScript 5 internal IsCallable function  
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");  
    }  

    var aArgs = Array.prototype.slice.call(arguments, 1),   
        fToBind = this,   
        fNOP = function () {},  
        fBound = function () {  
          return fToBind.apply(this instanceof fNOP  
                                 ? this  
                                 : oThis || window,  
                               aArgs.concat(Array.prototype.slice.call(arguments)));  
        };  

    fNOP.prototype = this.prototype;  
    fBound.prototype = new fNOP();  

    return fBound;  
  };  
} 
this.dom.addEventListener("click", function(event) {
    self.onclick(event)
}, false);

на jQuery пользователи ищут решение этой проблемы, вы должны использовать jQuery.доверенности

объяснение в том, что self.onclick не означает, что вы думаете, что это означает в JavaScript. Это на самом деле означает ).

JavaScript имеет только функции и никаких делегатов, таких как C#, поэтому невозможно передать метод и объект, к которому он должен быть применен в качестве обратного вызова.

единственный способ вызвать метод обратного вызова назвать себя внутри обратного вызова функция. Поскольку функции JavaScript являются замыканиями, они могут обращаться к переменным, объявленным в области, в которой они были созданы.

var obj = ...;
function callback(){ return obj.method() };
something.bind(callback);

хорошее объяснение проблемы (у меня были проблемы с пониманием решений, описанных до сих пор) - это здесь.

Я написал этот плагин...

Я думаю, что это будет полезно

jquery.обратный вызов

Это одна из самых запутанных точек JS: переменная "this" означает самый локальный объект... но функции также являются объектами, поэтому " это " указывает там. Есть и другие тонкие моменты, но я не помню их всех.

Я обычно избегаю использовать "это", просто определите локальную переменную " me " и используйте ее вместо этого.