JavaScript. ¿Para qué sirven los closures?

En el pasado artículo sobre programación, explicaba qué son los closures de JavaScript. Resumiendo mucho, podríamos decir que los closures en JavaScriptestán compuestos por una función (llamémosla externa) que contiene otra función (llamémosla interna). La función interna tiene acceso a las variables de la función externa, creando un contexto que es persistente a través de distintas llamadas. Si la definición os parece enrevesada, echarle un ojo al artículo que mecioanba antes, ya que con ejemplos se entiende mucho mejor.

Y una vez que sabemos qué son los closures, veamos cómo podemos utilizarlos.

Variables públicas y privadas

En JavaScript, no tenemos ningúna palabra reservada que permita declarar variables o funciones como privadas. Vamos que no existe un private que restringa la visibilidad de un método o propiedad fuera del objeto. Pero para eso podemos utilizar los closures y lo que se conoce como el Yahoo Module Pattern.

varPerson=function(){
    // Esta variable es privada
    varsavedName=anónimo;return{
        getName: function(){
            returnsavedName;
        },
        setName: function(name){
            savedName=name;
        }
    }
}();

En el ejemplo estamos creando una nueva función anónima con una variable que será privada, y un return que devuelve las dos funciones JavaScript internas. La variable es privada, porque nosotros no tenemos acceso desde fuera de la función, pero las funciones que ha devuelto el return sí lo tienen, ya que están en el mismo contexto que la función exterior. En definitiva, las variables están dentro del closure y las funciones internas tienen acceso a ellas. Para terminar de aclarar el concepto, un ejemplo de la ejecución.

//Escribe Hola undefined, porque no tenemos acceso a la variable
privadadocument.write(Hola+Person.savedName);
//Escribe Hola anónimo, porque la variable tiene todavía el valor por
defectodocument.write(Hola+Person.getName());
//Cambiamos el nuevo valor de la variable privada
Person.setName(Cylon);
//Escribe Hola Cylon
document.write(Hola+Person.getName());

Esta funcionalidad de los closures es muy útil, y utilizando este patrón, podemos crear código JavaScript más robusto, controlando a que propiedades o métodos se puede llamar, y cuáles no.

Funciones que se llaman a si mismas

Está es una aplicación curiosa de los closures. Veamos un ejemplo:

(function(name){
    document.write(Hola+name);
})(Cylon);

En este caso, la función anónima que está dentro del paréntesis, y que recibe un parámetro, devuelve un objeto. Este objeto es una función con su contexto, que en este caso es la variable name. Justo después, con otro paréntesis, hacemos la llamada a dicha función pasándole el valor para el parámetro name. En realidad es una manera más rápida de hacer lo mismo que hacíamos en el anterior artículo, al explicar los closures:

varexample=function(name){
    returnfunction(){
        document.write(Hola+name);
    }
};
varhelloCylon=example(Cylon);
helloCylon();

Así que con las funciones que se llaman a si mismas, tenemos un mecanismo interesante para ejecutar código bien encapsulado.

Funciones generadoras

Como veíamos en el artículo anterior, con el ejemplo de la hucha, podemos utilizar los closures para generar objetos totalmente independientes a partir del mismo código. Veamoslo con un ejemplo distinto:

function warnMessage(message) {
    return function () {
        alert(Warning:  + message);
    }
};

$(document).ready(function () {

    var message1 = warnMessage(El nombre es obligatorio);
    var message2 = warnMessage(La edad es obligatoria);

    $(“#name).click(message1);
    $(“#age).click(message2);

});

Hemos creado una funcion llamada warnMessage, que es la encargada de crear una función que lanzará un alertcon un determinado mensaje. Luego creamos dos objetos distintos, con la función generadora y los asociamos directamente al evento click de JQuery. Cuando se haga clik en esos elementos, que pueden ser botones, se mostrará el mensaje especificado. Muy útil.

Bonustrack. El problema de los bucles.

A veces, sin darnos cuenta, podemos crear closures de forma involuntaria, generando comportamientos que no esperamos.

function linkGenerator(linkNumber) {
    for (var i = 1, link; i <= linkNumber; i++) {
        link = document.createElement(a);
        link.innerHTML = Link  + i;
        link.onclick = function () {
            alert(i);
        };
        document.body.appendChild(link);
    }
};

La funcion linkGenerator es una función generadora de enlaces. Cuándo la llamamos, añade a nuestra página tantos enlaces como indiquemos en el parámetro linkNumber. Si la ejecutamos en nuestra página, veremos que los enlaces se crean sin problemas aparentes. Pero en realidad, hay un problema. Si hacemos clik en los enlaces, se ejecutará la función onclick y se mostrará siempre el mismo número. Concretamente el  último valor de la variable i. ¿Por qué sucede esto? Pues porque si nos fijamos bien, sin querer, hemos creado un closure. Dentro de una función estamos creando otra función, que no se ejecuta hasta que se pincha en el enlace. Como los closures guardan el contexto, el enlace mostrará el último valor que había tomado la variable interna i.

Así que cuidado con crear closuresde forma involuntaria.

Conclusiones

Los closures no son fáciles de entender. Los podemos crear de diferentes maneras, incluso involuntariamente. Hay que ser cuidadosos a la hora de utilizarlos para no cometer errores. Pero una vez que los hayamos entendido tendremos una potente herramienta para ayudarnos en diversas tareas. En especial a la hora de crear métodos y variables privados.



¿Quiéres que te avisemos cuando se publiquen nuevas entradas en el blog?

Suscríbete por correo electrónico o por RSS