Você está na página 1de 10

Closures en Javascript

http://www.variablenotfound.com/2012/10/closures-en-javascript-entiendelos-de.html

Los closures, en castellano cierres o clausuras, son una potente caracterstica de


JavaScript que tambin podemos encontrar en otros lenguajes como C#, Objective-C,
Eiffel y otros. Este concepto, que tradicionalmente pertenece a la programacin
funcional, tiene su origen en el lenguaje de programacin Scheme.
En este artculo vamos a comenzar por identificar en el cdigo cundo se producen los
closures para luego ver qu tienen de especial. Despus pasaremos a ver algunos
comportamientos que nos servirn para terminar completamente de entender cmo
funcionan. Para finalizar, veremos un par de ejemplos de aplicaciones prcticas.

Concepto de closure
Cuando nos acercamos por primera vez al concepto de closure es habitual
encontrarnos con problemas a la hora de entenderlo, a pesar de que seguramente en
muchas ocasiones los habremos utilizado sin saberlo. Por este motivo, antes de
intentar dar una definicin ms o menos acadmica, creo que es mucho ms prctico
saber identificar los closures en el cdigo, que al fin y al cabo es el lenguaje natural del
programador.

Identifiquemos los elementos que se producen en el cdigo anterior.

En primer lugar podemos ver claramente que tenemos dos funciones, y que la
funcin greet() est definida dentro de la funcin personalizedGreet().
En segundo lugar podemos observar como la funcin greet() utiliza en su
cuerpo una variable local perteneciente al mbito de la funcin
personalizedGreet().

Siempre que se produzcan estos dos elementos podemos decir que tenemos un
closure entre manos. As de sencillo! Comprobemos ahora cmo funciona el cdigo
que hemos visto en la imagen anterior.

Debemos comentar aqu que la apariencia ms habitual de los closures es cuando


utilizamos funciones annimas, como en el siguiente ejemplo:

Acabamos de ver cmo identificar en el cdigo cuando se producen los closures


pero...qu tienen de particular?, qu hace tan especial al cdigo anterior?
Analicemos paso a paso lo que sucede supuestamente cuando se ejecuta la funcin
whatAreYouWritingAbout():
1. Comienza la ejecucin de la funcin whatAreYouWritingAbout().
2. Se guarda en memoria la variable subject con el valor string "About
Closures".
3. Se guarda en memoria la variable message con una referencia al cdigo:
function () { return subject; }.
4. La funcin whatAreYouWritingAbout() devuelve el cdigo anterior.
5. Fin de la ejecucin de la funcin whatAreYouWritingAbout().
6. Se llama al Garbage Collector para que elimine la variable subject, dado que
la funcin ha terminado y no la necesitar hasta su prxima ejecucin, donde
ser otra vez definida y asignada.
Segn lo expuesto anteriormente cuando ejecutamos las lneas de cdigo siguientes:

Literalmente es como si estuviramos escribiendo lo siguiente.

Pero evidentemente, en este contexto o mbito del cdigo, la variable subject no


existe (o est fuera de mbito) y adems su valor supuestamente fue destruido al
salir de la funcin whatAreYouWritingAbout(). Por lo tanto, cuando se ejecuta la
funcin writingAbout() debera dar un error tipo Uncaught ReferenceError:
subject is not defined. Sin embargo, el cdigo muestra el mensaje correctamente...
Por
qu?.
Cuando JavaScript encuentra en el cdigo un closure le indica al Garbage Collector
que no destruya las variables (estn quedan guardadas o encerradas) que la
funcin "interna" necesita para su correcta ejecucin. Por lo tanto, a pesar de que
las variables que esta funcin utiliza se encuentran en otro mbito en el momento de
su ejecucin, JavaScript guard una referencia al valor de las mismas y por lo tanto
siempre estn accesibles para la funcin. Podramos decir que este es el secreto o la
magia de los closures.

Comportamiento
Para terminar de captar el concepto veamos ahora una serie de comportamientos o
caractersticas que tienen los closures y que tendremos que tener siempre presentes
cuando trabajamos con esta potente herramienta del lenguaje JavaScript.
Cmo se crean los closures
Cuando el intrprete de JavaScript encuentra un closure guarda las variables locales
que las funciones internas van a necesitar. Esto ya lo hemos comentado, pero...
cmo acceden las funciones a estas variables? Veamos algunos apuntes sobre esto:

Cuando una variable local se almacena en memoria, si sta cambia a lo largo


de la ejecucin de la funcin externa, en memoria se almacenar el ltimo valor
asignado al salir de la funcin externa.
Cada funcin interna guarda una referencia a la posicin de memoria donde se
almacena la variable, pudiendo acceder a su valor.

La idea principal es que el valor de la variable puede ser modificada a lo largo del
cdigo y que las funciones internas guardan una referencia a la memoria donde
se encuentra el ltimo valor de la variable que fue asignada al salir de la funcin
externa. Es importante tener esto presente y, como veremos ahora, tiene algunos
comportamientos que en principio podran sorprender.
Definir o modificar la variable despus de la funcin interna
En los ejemplos de cdigo que hemos comentado anteriormente la variable local de la
funcin externa se encontraba definida antes que la funcin anidada. Lo contrario es
perfectamente vlido.

Como podemos observar no tiene ninguna influencia cuando es definida la variable


local, la funcin interna guarda una referencia a la posicin de memoria y utiliza el
valor de la variable cuando la necesita. El lector puede leer ms sobre este efecto y su
importancia en el siguiente enlace: http://www.jasoft.org/Blog/post/Elevacion-devariables-%28hoisting%29-en-JavaScript.aspx
Por cierto, en este ejemplo la funcin interna annima utiliza realmente dos variables
locales, hello y name. Los parmetros de la funcin externa son considerados
lgicamente como variables de este mbito. Anteriormente se omiti este hecho para
simplificar el concepto y explicacin.

En el siguiente ejemplo tenemos una funcin que define en su interior una constante
(con valor 10) que ser incrementada en una cantidad pasada como parmetro a la
funcin:

Pensad por un momento cual ser la salida en pantalla de este cdigo, 10 15. Los
que hayis respondido 15 estis en lo cierto. Los que no, es normal, se tiende a
pensar que el closure queda establecido en el comento que se define la funcin
anidada, pero no es as. Como ya hemos comentado, la funcin interna
simplemente almacena una referencia al ltimo valor de la variable establecido
cuando la funcin externa termina de ejecutarse.
Cada llamada es un closure distinto
Visto lo anterior podra pensarse que el closure es un elemento global que persiste
entre llamadas. No es as, por cada llamada que hagamos a la funcin externa
estaremos creando un nuevo e individual closure. Veamos el ejemplo siguiente.

Cuando tenemos varias funciones internas


Veamos ahora un ejemplo en el que varias funciones internas guardan una referencia
a la misma variable local, o podramos decir tambin que mantienen una referencia al
mismo closure. En este ejemplo la funcin externa no devuelve ningn objeto, vamos
almacenar las funciones internas en variables globales.

Piense el lector por un momento cul ser la salida de este script. Efectivamente, la
salida son tres mensajes seguidos con el texto "Toma ya!!". Notar que las tres
funciones internas mantienen una referencia al mismo valor, es decir, las tres
variables globales pertenecen al mismo closure. De hecho, este podra ser el caso
del esquema que hemos visto en la imagen cuando hablbamos de como se crean los
closures.
Pero compliquemos el cdigo un poco.

Si volvemos a ejecutar el cdigo de la funcin gExecute1() obtenemos el mensaje


"Toma ya!!". Esto es totalmente lgico porque esta funcin tiene una referencia al
mismo closure que gExecute3() que fue la ltima funcin en modificar el closure que
comparten.

Toda variable puede pertenecer al closure


Debemos tener en cuenta que todo tipo de variable que se declare dentro del
mbito de la funcin externa y sea utilizada por la funcin anidada pertenecer al
closure. Esto puede dar algunos resultados inesperados por creacin de closures no
previstos. Por ejemplo, cuando ejecutamos bucles debemos prestar atencin si dentro
de la funcin interna utilizamos la variable contador del bucle: sta es a todos los
efectos una variable local de la funcin externa y por lo tanto pertenecer al closure.

Como podemos observar el contador tambin pertenece al closure y est disponible


para la funcin interna fuera del mbito de la funcin externa. Adems, y aqu es
cuando tenemos que tener cuidado, el ltimo valor para esta variable es 6 porque el
bucle ejecuta una vez ms la instruccin i++ para luego realizar la comprobacin i <=
length y salir del mismo.

Aplicaciones prcticas
Ahora que seguramente que ya hemos entendido cmo se comportan los closures,
pasemos a algo ms prctico. En seguida vamos a ver algunos ejemplos donde
podemos comprobar en el campo de batalla la potencia de esta caracterstica del
lenguaje. Tenga el lector en cuenta que aplicaciones sobre este concepto pueden ser
tantas como las necesidades o imaginacin de los programadores, aqu veremos solo
algunas.
Ejecutar funciones retardadas
Quizs este sea el ejemplo prctico ms clsico y que el lector seguramente ya
conocer. En ocasiones en nuestro cdigo JavaScript podemos necesitar realizar
operaciones con retardo, para ello JavaScript nos provee de las funciones
setTimeout() y setInterval().

Normalmente llamaremos a estas funciones pasndole como parmetros el nombre de


la funcin a ejecutar y el tiempo de retardo en milisegundos. El problema a resolver es
que sucede si la funcin que queremos ejecutar con retardo toma uno o ms
parmetros. En principio no tenemos forma de pasarle los parmetros a nuestra
funcin dado que setTimeout() y setInterval() slo admiten el nombre de la
funcin como primer parmetro. Esto es fcil de resolver con un closure.

Acceso desde miembros privados a miembros pblicos


En un artculo anterior cuando hablamos de las funciones constructoras comentamos
que desde los mtodos privados no podamos tener acceso a las propiedades pblicas
del objeto creado. Retomando el ejemplo de cdigo que vimos en aquel artculo
veamos cmo saltarnos este pequeo escollo:

Personalizar eventos para objetos determinados del DOM

Supongamos que queremos encapsular en objetos del DOM la tpica funcionalidad


para que cuando el ratn se posicione sobre un elemento HTML ste cambie el texto
de color y el estilo del puntero, mientras que cuando el ratn abandone el elemento, el
texto y puntero vuelven a su estado anterior.

1. Aqu es donde se produce el closure. Como se puede apreciar la funcin


interna guardar una referencia a un objeto pasado como parmetro y al
nombre de una funcin que en este caso ser el nombre de nuestro evento
personalizado. Ntese una vez ms, aqu simplemente se est creando el
closure y devolviendo un cdigo, que ser ejecutado ms tarde, parecido a:
objeto.eventoPersonalizado(elementoHTML, eventoDOM) . Aunque este
cdigo se ejecute despus, la funcin seguir teniendo acceso al objeto y al
valor de la cadena que representa el nombre del evento.
2. Aunque la palabra reservada this pueda parecer que se utiliza aqu,
recordemos que en este momento slo se crea el closure y se devuelve un
cdigo que ser ejecutado a posteriori. En el momento de ejecutarte este
cdigo, cuando un evento del DOM se dispare (ej: onclick), this har
referencia al elemento HTML que dispara el evento del DOM.
3. En este momento asignamos a los eventos del DOM que nos interese el cdigo
devuelto por funcDelegate. Lo hemos mencionado antes, este cdigo ser del
estilo: objeto.eventoPersonalizado(elementoHTML, eventoDOM). Por lo
tanto, cuando se dispare un evento del DOM, como el onclick, estaremos
llamando a una funcin que hace las veces de nuestro evento
personalizado.
4. Aqu this hace referencia al objeto creado con la funcin constructora
DOMObject(id).

Seguramente el lector conozca otras formas ms sencillas por medio de llamadas


sucesivas a funciones de implementar el mismo comportamiento. La ventaja de
hacerlo de esta forma es que estamos encapsulando la funcionalidad con los propios
objetos del DOM, lo que nos proporciona ms robustez y mantenibilidad del cdigo y
adems nos permite disparar los eventos personalizados cuando lo necesitemos:

Referencias

Muy recomendable es la lectura del libro "Fundamentos de JavaScript y AJAX


para desarrolladores y diseadores web", en el que se puede encontrar una
inteligente tcnica para encapsular miembros de objetos por medio de closures
adems de ser un completsimo tratado sobre JavaScript.
Si el lector quiere profundizar ms sobre este concepto le recomiendo el
siguiente enlace: http://dmitrysoshnikov.com/ecmascript/chapter-6-closures/
Al final del siguiente enlace se utiliza un patrn por medio de closures para
implementar propiedades privadas, pblicas y con privilegios en objetos
JavaScript: http://www.crockford.com/javascript/private.html

Autor: scar Sotorro Snchez

Você também pode gostar