var myObject = { message: “Hello!”, talk: function() { alert(“I say: ” + this.message); } }cuando hacemos referencia a
this.message
estamos apuntando a la propiedad message en el contexto del objeto myObject. Por lo tanto this es una referencia al objeto en el que estamos.Si hacemos:
myObject.talk();
veremos el mensaje:
"I say: Hello"
Parece fácil, pero se complica y mucho.
El contexto de un callback
Supongamos que queremos utilizar el métodomyObject.talk()
como callback o event handler que se ejecuta al pulsar un botón:$(".button1").click( myObject.talk );Al hacer click en el botón veremos:
"I say: undefined"
¡¿Undefined?!. Cuando se llama al método
myObject.talk
al hacer click en el botón, el valor de this.message es undefined. La razón es que this ya no apunta al objeto myObject, hace referencia a otro contexto diferente. En este caso, al ser un callback de un evento del DOM, el navegador asocia this al elemento que provoca el evento ( si usamos addEventListener, que es la opción que utiliza jQuery si el navegador lo permite ). Es decir, this es el objeto button. Como dentro de este objeto no hay ninguna propiedad que se llame message, this.message es undefined.
No es el único caso en que se cambia el contexto. Si lo usamos como callback de una llamada AJAX o de setTimeout() tampoco tendrá el valor original.
Y por si las cosas no fueran ya bastante complicadas, resulta que si ponemos el método dentro de una función anónima, el mensaje es correcto:
$(".button1").click(function() { myObject.talk(); });
El resultado es:
"I say: Hello!"
Es casi igual que lo que teníamos antes, llamámos al método
myObject.talk()
pero ahora dentro de una función ¿por qué ahora sí funciona?. Bueno, el valor de this ahora sí es el objeto myObject. Lo explicaremos un poco más adelante.Vamos a ver como se comporta el puntero this en funciones/métodos y después volveremos sobre este ejemplo para explicar qué es lo que está pasando.
this en funciones y métodos
Cuando tenemos una referencia a this dentro de una función, su valor dependerá siempre de cómo se llama a la función.
Por ejemplo, si hacemos:
var f1 = myObject.talk; f1(); //this.message is undefinedEstamos llamando a la función
talk()
de una forma diferente, desde fuera del objeto. En este caso this será el contexto desde el que ejecutamos f1()
, que es el objeto global (window).Sin embargo, si ejecutamos la función como un método de un objeto concreto, el valor de this será ese objeto:
myObject.talk(); //this.message is "Hello!"
Podríamos incluso llamar a la misma función desde otro objeto diferente:
var Object2 = { message: "I'm OBJECT2", } Object2.talk = myObject.talk; Object2.talk(); //this.message is "I'm OBJECT2"
En el alert en pantalla veremos:
"I say: I'm OBJECT2"
Volviendo al ejemplo del callback
Ahora podemos entender qué estaba pasando en nuestros dos ejemplos iniciales. Recordemos que teníamos la funcióntalk()
como callback de dos formas diferentes://Example 1 $(".button1").click( myObject.talk ); //Example 2 $(".button1").click(function() { myObject.talk(); });En el primer caso le estamos pasando una referencia directa a nuestra función, para que la ejecute cuando y como quiera. Como hemos visto antes, el valor de this dependerá de cómo se llame a la función. Nosotros ya no tenemos el control sobre cómo se la llamará, por eso el valor del contexto ya no es nuestro objeto.
El navegador, cuando llame al callback, colocará el contexto del objeto del DOM que ha lanzado el evento.
En el segundo caso se llama a la función anónima también con el contexto del elemento button, pero dentro de esta función estamos ejecutando explícitamente un método del objeto myObject:
myObject.talk();
Es una llamada directa al método dentro del objeto concreto y, como hemos visto antes, el contexto será el del objeto que se referencia al llamarlo. Por eso aquí el puntero this es correcto.
Hay varias formas de asegurarnos de que el contexto con el que se llama a una función es el que nosotros queremos, como Function.apply(), Function.call() o utilizando una función proxy, pero esto lo veremos en otro post.
Fuentes:
MDN This
No hay comentarios:
Publicar un comentario