JavaScript Callback Âmbito
-
06-07-2019 - |
Pergunta
Eu estou tendo alguns problemas com o bom e velho JavaScript (sem quadros) em referência a meu objeto em uma função de retorno de chamada.
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;
}
};
Agora, quando eu criar um novo objeto (após o DOM foi carregado, com um teste de extensão #)
var x = new foo('test');
O 'isto' dentro da função onclick aponta para o teste de extensão # e não o objeto foo.
Como faço para obter uma referência para o meu objeto foo dentro da função onclick?
Solução
(extraído alguma explicação que estava escondida em comentários em outra resposta)
As mentiras problema na seguinte linha:
this.dom.addEventListener("click", self.onclick, false);
Aqui, você passar um objeto função a ser usado como retorno. Quando o disparador do evento, a função é chamada, mas agora ele não tem nenhuma associação com qualquer objeto (this).
O problema pode ser resolvido por envolvimento da função (com a sua referência de objecto) em um fecho como se segue:
this.dom.addEventListener(
"click",
function(event) {self.onclick(event)},
false);
Uma vez que o auto variável foi atribuído este quando o fechamento foi criado, a função de fechamento vai se lembrar do valor da variável auto quando é chamado em um momento posterior.
Uma forma alternativa de resolver isso é fazer uma função de utilidade (e evitar o uso de variáveis ??para a ligação este ):
function bind(scope, fn) {
return function () {
fn.apply(scope, arguments);
};
}
O código atualizado seria semelhante:
this.dom.addEventListener("click", bind(this, this.onclick), false);
Function.prototype.bind
faz parte do ECMAScript 5 e fornece a mesma funcionalidade. Então você pode fazer:
this.dom.addEventListener("click", this.onclick.bind(this), false);
Para navegadores que não suportam ES5 ainda, MDN fornece o seguinte calço :
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;
};
}
Outras dicas
this.dom.addEventListener("click", function(event) {
self.onclick(event)
}, false);
Para o jQuery usuários que estão buscando uma solução para este problema, você deve usar jQuery.proxy
A explicação é que self.onclick
não significa o que você acha que isso significa em JavaScript. É realmente significa a função onclick
no protótipo do objeto self
(sem de modo algum referência a si self
).
JavaScript só tem funções e nenhum delegado como C #, por isso não é possível passar um método eo objeto deve ser aplicado como um callback.
A única maneira de chamar um método em um callback é chamá-lo a si mesmo dentro de uma função de chamada de retorno. Porque funções JavaScript são fechamentos, eles são capazes de acessar as variáveis ??declaradas no âmbito eles foram criados em.
var obj = ...;
function callback(){ return obj.method() };
something.bind(callback);
Uma boa explicação do problema (eu tinha problemas em entender as soluções descritas até agora) é disponível aqui .
este é um dos mais confusos pontos de JS: os 'isto' variável significa para o objeto mais locais ... mas funções também são objetos, de modo que 'isto' pontos lá. Há outros pontos sutis, mas eu não me lembro de todos eles.
Eu costumo evitar o uso de 'isto', apenas definir um 'eu' variável local e utilizá-lo.