traduccion - Javascript Closures y ''esto''
javascript prototype (5)
Tengo un problema con un objeto que he creado que se ve así:
var myObject = {
AddChildRowEvents: function(row, p2) {
if(document.attachEvent) {
row.attachEvent(''onclick'', function(){this.DoSomething();});
} else {
row.addEventListener(''click'', function(){this.DoSomething();}, false);
}
},
DoSomething: function() {
this.SomethingElse(); //<-- Error here, object ''this'' does not support this method.
}
}
El problema es que cuando estoy dentro de la función ''DoSomething'', ''esto'' no se refiere a ''myObject'' ¿qué estoy haciendo mal?
Cuando se llama a la función, "esto" se refiere a la fila. Si quieres tener el objeto, puedes hacerlo de la siguiente manera:]
AddChildRowEvents: function(row, p2) {
var theObj = this;
if(document.attachEvent) {
row.attachEvent(''onclick'', function(){theObj.DoSomething();});
} else {
row.addEventListener(''click'', function(){theObj.DoSomething();}, false);
}
},
Cuando se llama a la función, tiene acceso a la variable theOBj que estaba en el alcance cuando se definió la función.
El problema con su código está aquí, en la siguiente línea de código definió una función anónima y la pasó como un controlador de eventos para el evento onClick, en las siguientes líneas:
row.attachEvent(''onclick'', function(){this.DoSomething();});
y
row.addEventListener(''click'', function(){this.DoSomething();}, false);
cuando se activa el evento onclick y llama a la función anónima que ha pasado, este contexto se refiere al objeto que se ha planteado el evento, pero no myObject . porque en este punto, el contexto de ejecución está en el contexto del controlador de eventos.
La solución: puede hacer el truco que se le ha dicho a Mike Kantor, o usar jQuery, usar el método proxy, para definir este contexto en la función anónima.
Entonces tu código sería así:
var myObject = {
AddChildRowEvents: function(row, p2) {
if(document.attachEvent) {
row.attachEvent(''onclick'', $.proxy(function () {
this.DoSomething();
}, this));
} else {
row.addEventListener(''click'', $.proxy(function () {
this.DoSomething();
},this), false);
}
},
DoSomething: function() {
this.SomethingElse(); //<-- Error here, object ''this'' does not support this method.
}
}
usted ha mencionado que el error está en esto.SomthingElse () , pero su código no lo muestra. Si realmente obtiene un error en esa línea de código, puede que esté utilizando el método DoSomthing en otro lugar como controlador de eventos.
El problema proviene de cómo this
se gestiona en JS, lo que puede ser rápido, especialmente cuando se le llama de manera asíncrona (y cuando se crea un cierre, vinculado a esto).
Cuando se llama al método AddChildRowEvents, this
referencia bien a la variable myObject
. Pero el objetivo de este método es poner un controlador para hacer clic. Entonces el evento se activará en algún momento en el futuro. En ese momento, ¿qué objeto realmente desencadenará el evento? De hecho, es el DOM element
en el que el usuario hará clic. Entonces this
hará referencia a este DOM element
y no a la variable myObject
.
Para una solución más moderna que las presentadas, se puede usar el bind method
o la arrow function
.
1. Solución con función de flecha
let handler = {
addEventHandler: function(row) {
console.log("this", this)
row.addEventListener("click", () => {
console.log("this", this)
this.doSomethingElse()
})
},
doSomethingElse: function() {
console.log("something else")
}
}
var div = document.querySelector("div")
handler.addEventHandler(div)
<div>one</div>
Con la arrow function
(que está disponible desde ES2015
), this
contexto no está en el contexto de ejecución sino en el contexto léxico. La diferencia entre los dos es que el contexto léxico se encuentra en el tiempo de compilación no en el tiempo de ejecución, por lo que el contexto léxico es más fácil de rastrear. Aquí, dentro de la arrow function
, this
referencia al mismo this
a la función externa (es decir, addEventHandler
), de modo que hace referencia a myObject
.
Para obtener más información sobre las propiedades de la función de flecha de grasa en comparación con las funciones normales, https://dmitripavlutin.com/6-ways-to-declare-javascript-functions/
2. Solución con enlace
let handler = {
addEventHandler: function(row) {
console.log("this", this)
row.addEventListener("click", function() {
console.log("this", this)
this.doSomethingElse()
}.bind(this))
},
doSomethingElse: function() {
console.log("something else")
}
}
var div = document.querySelector("div")
handler.addEventHandler(div)
<div>one</div>
Esta vez, cuando se pasa la función de devolución de llamada a addEventListener
, se ha vinculado this
contexto a this
elemento en la función addEventHandler
externa.
Este es un problema común con los cierres. Para resolverlo, pruebe algo como esto:
var myObject = {
AddChildRowEvents: function(row, p2) {
var self = this;
if(document.attachEvent) {
row.attachEvent(''onclick'', function(){this.DoSomething(self);});
} else {
row.addEventListener(''click'', function(){this.DoSomething(self);}, false);
}
},
DoSomething: function(self) {
self.SomethingElse();
}
}
this
siempre se refiere a la función interna, si tiene funciones anidadas, debe crear otra variable y señalar eso.
var myObject = {
AddChildRowEvents: function(row, p2) {
var that = this;
if(document.attachEvent) {
row.attachEvent(''onclick'', function(){that.DoSomething();});
} else {
row.addEventListener(''click'', function(){that.DoSomething();}, false);
}
}
}