w3schools mdn javascript events binding scope callback

mdn - callback javascript w3schools



Alcance de devoluciĆ³n de llamada de JavaScript (7)

Escribí este complemento ...

creo que será útil

jquery.callback

Estoy teniendo problemas con el JavaScript simple antiguo (sin marcos) al hacer referencia a mi objeto en una función de devolución de llamada.

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; } };

Ahora cuando creo un objeto nuevo (después de que el DOM se haya cargado, con una prueba span #)

var x = new foo(''test'');

El ''this'' dentro de la función onclick apunta a la prueba span # y no al objeto foo.

¿Cómo obtengo una referencia a mi objeto foo dentro de la función onclick?


La explicación es que self.onclick no significa lo que crees que significa en JavaScript. Significa en realidad la función onclick en el prototipo del self del objeto (sin referirse en modo alguno a self ).

JavaScript solo tiene funciones y no hay delegados como C #, por lo que no es posible pasar un método Y el objeto al que se debe aplicar como devolución de llamada.

La única manera de llamar a un método en una devolución de llamada es llamarlo usted mismo dentro de una función de devolución de llamada. Como las funciones de JavaScript son cierres, pueden acceder a las variables declaradas en el ámbito en el que se crearon.

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


Para los usuarios de jQuery que buscan una solución a este problema, debe usar jQuery.proxy


Una buena explicación del problema (tuve problemas para entender las soluciones descritas hasta ahora) está disponible aquí .


este es uno de los puntos más confusos de JS: la variable ''this'' significa el objeto más local ... pero las funciones también son objetos, por lo que ''this'' apunta allí. Hay otros puntos sutiles, pero no los recuerdo a todos.

Normalmente evito usar ''esto'', simplemente defino una variable ''me'' local y la uso en su lugar.


(extrajo alguna explicación que estaba oculta en los comentarios en otra respuesta)

El problema radica en la siguiente línea:

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

Aquí, pasas un objeto de función para usar como devolución de llamada. Cuando se activa el evento, se llama a la función, pero ahora no tiene asociación con ningún objeto (esto).

El problema puede resolverse envolviendo la función (con su referencia de objeto) en un cierre de la siguiente manera:

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

Como la variable self se asignó a esto cuando se creó el cierre, la función de cierre recordará el valor de la variable auto cuando se la llame en un momento posterior.

Una forma alternativa de resolver esto es hacer una función de utilidad (y evitar el uso de variables para vincular esto ):

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

El código actualizado se vería así:

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

Function.prototype.bind es parte de ECMAScript 5 y proporciona la misma funcionalidad. Entonces puedes hacer:

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

Para los navegadores que aún no son compatibles con ES5, Function.prototype.bind :

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);