Область Обратного Вызова 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 ответов:
(извлечено некоторое объяснение, которое было скрыто в комментариях в другом ответе)
проблема заключается в следующей строке:
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; }; }
на jQuery пользователи ищут решение этой проблемы, вы должны использовать jQuery.доверенности
объяснение в том, что
self.onclick
не означает, что вы думаете, что это означает в JavaScript. Это на самом деле означает ).JavaScript имеет только функции и никаких делегатов, таких как C#, поэтому невозможно передать метод и объект, к которому он должен быть применен в качестве обратного вызова.
единственный способ вызвать метод обратного вызова назвать себя внутри обратного вызова функция. Поскольку функции JavaScript являются замыканиями, они могут обращаться к переменным, объявленным в области, в которой они были созданы.
var obj = ...; function callback(){ return obj.method() }; something.bind(callback);
хорошее объяснение проблемы (у меня были проблемы с пониманием решений, описанных до сих пор) - это здесь.
Это одна из самых запутанных точек JS: переменная "this" означает самый локальный объект... но функции также являются объектами, поэтому " это " указывает там. Есть и другие тонкие моменты, но я не помню их всех.
Я обычно избегаю использовать "это", просто определите локальную переменную " me " и используйте ее вместо этого.