Últimamente mi buen amigo Álex me está animando a que dé una oportunidad a CoffeeScript, el preprocesador de JavaScript que más está triunfando. Hablando de todo un poco, hemos comentado que JavaScript tiene algunas peculiaridades que, cuando uno empieza a trabajar con él, le descoloca un poco. Por ejemplo, el hecho de que su orientación a objetos esté basada en prototipos. Otra de esas peculiaridades que han salido a relucir es el cómo se manejan los ámbitos en eventos JavaScript. O dicho de otra forma: ¿Dónde debería apuntar this cuando estamos en la función manejadora de un evento?

En JavaScript this siempre apunta al dueño de la función en ejecución.

Revisa y prueba el siguiente ejemplo (abre antes la consola javascript de tu navegador):

foo() se ejecuta en tres casos:

  1. El primero sucede al cargarse la página, siendo this el objeto window, y dado que window no tiene ninguna propiedad llamada style, nada sucede.
  2. El segundo caso sucede cuando hacemos clic sobre el elemento <h1>. this es ahora el objeto <h1>, y dado que este sí tiene la propiedad style, su color se vuelve rojo.
  3. En el tercero, es cuando hacemos clic sobre el elemento <h2>. Pero no ocurre nada.

¿Es lógico que en unos casos this referencie a un objeto y en otros a otro? ¿No habíamos dicho que this siempre apunta al dueño de la función en ejecución? Porque parece evidente que, al haber definido a foo en el espacio global, esta función siempre pertenece a window. Sin embargo, como hemos comprobado, parece que no es esto lo que sucede.

Lo que está ocurriendo es que en unos casos estamos pasando una copia de foo(). Este es el caso cuando hacemos clic sobre el elemento h1: addEventListener(‘click’, foo). Al ser una copia, el contexto cambia. La nueva función foo ahora pertenece al objeto h1.

En el caso del elemento h2 declaramos una función anónima (esta función anónima se copia/pertenece al objeto h2), que a su vez invoca a la función global foo(). Por tanto, en esta ocasión tenemos una referencia a la función foo() del objeto window.

Vamos a ver algunos ejemplos que extraigo de quirksmode:

Por copia

element.onclick = doSomething
element.addEventListener('click',doSomething,false)
element.onclick = function () {this.style.color = '#cc0000';}
<element onclick="this.style.color = '#cc0000';">

Por referencia

element.onclick = function () {doSomething()}
element.attachEvent('onclick',doSomething)
<element onclick="doSomething()">

El método attachEvent de las versiones antiguas de IE, a diferencia de addEventListener, pasa una referencia.

Elegir contexto

A la hora de la verdad, lo usual (y lo que suele ser aconsejable) es utilizar los métodos que pasan la función por copia. De hecho la propuesta del w3c es emplear addEventListener. Sin embargo, en algunos casos, ya sea por necesidad, o por legibilidad, quizás sea preferible que el ámbito apunte a un objeto diferente al que emitió el evento.

JavaScript

Desde JavaScript 1.8.5 tenemos el método bind que nos permite precisamente especificar esto:

Creamos un objeto, con 3 componentes:

  1. Un atributo id.
  2. Un método printId que imprime el id de this.
  3. Un método main que añade manejadores para los eventos click de cada uno de los elementos h1.

Al hacer clic sobre el primer h1, this.id apunta al id del elemento h1. Sin embargo, al hacer clic sobre el segundo h1, apunta al id de Objeto. Esto lo logramos gracias al método bind.

jQuery

Si utilizamos, por ejemplo jQuery, es igualmente sencillo elegir sobre qué contexto queremos trabajar. Es decir, podemos definir a qué apuntará this.

Echa un vistazo al siguiente código:

Como ves, se trata del mismo ejemplo de antes, pero esta vez utilizando la función $.proxy de jQuery.

CoffeeScript

Donde más sencillo resulta es en CoffeeScript:

En este caso hemos definido dos métodos, printId1() y printId2(). La única diferencia es en la flecha (-> vs =>). En el primer caso this apunta al h1 y en el segundo forzamos un cambio al contexto actual.