Вопрос

У меня возникли проблемы с простым старым 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');

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

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

Это было полезно?

Решение

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

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

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

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

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

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

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

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

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.proxy

Объяснение заключается в том, что self.onclick не означает, что вы думаете, что это означает в JavaScript. Фактически это означает функцию onclick в прототипе объекта self (никоим образом не ссылаясь на сам self ).

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

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

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

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

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

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

jquery.callback

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top