JavaScript OO: 2. Closures

En esta entrada veremos qué son las closures, el sistema para encapsular la información en JavaScript.

waterhouse

archivado en: JavaScript / 17 junio, 2013 / taller:

Seguimos con la serie de JavaScript orientado a objetos.

Imaginemos que estamos desarrollando una web con distintas aplicaciones en JavaScript y llevamos ya unas 2.000 líneas de código solo en uno de los archivos. Un programador que está desarrollando otra parte lleva a su vez otras tropecientas mil, entre las que hay una en la que da el valor 5 a una variable que se llama de la misma manera que una variable mía que tiene por valor 10. ¡Catacroker!

En los lenguajes de programación orientados a objetos, esta situación no puede suceder ya que una de las premisas básicas de este sistema es la «encapsulación», esto es, la separación del código en compartimentos estancos a los que solo se puede acceder intencionadamente mediante algún método ex profeso: son los denominados getter cuando leen los valores y setter cuando los cambian.

Pues bien, en JavaScript podemos conseguir el mismo efecto mediante el concepto de closures, que se podría traducir como «cierres» y que consiste en delimitar el ámbito de las variables y funciones mediante llaves. Por ejemplo, en este caso podemos acceder a variableGlobal porque está fuera de la función sin estar encorchetada por ninguna llave y, claro está, desde miFuncion() también podemos acceder a la variable declarada dentro de ella.

var variableGlobal = 10;

function miFuncion() {

var variableLocal = 5;

console.log(variableLocal); // Esto da 5

console.log(variableGlobal); // Esto da 10

}

miFuncion();

Pero si intentáramos hacer algo así...

function miFuncion() {

var variableLocal = 5;

}

console.log(variableLocal);

nos diría que «Uncaught ReferenceError: variableLocal is not defined», es decir, que variableLocal no está definida, ya que está dentro de un par de paréntesis. Y lo mismo sucede con las funciones. Es decir, esto también nos daría error...

function miFuncion() {

var variableLocal = 5;

function miOtraFuncion() {

// Lo que sea

}

}

miOtraFuncion();

ya que miOtraFuncion() está dentro de un par de paréntesis.

En cambio, en este caso, estas funciones sí que pueden interactuar entre sí al compartir el mismo ámbito (scope, en inglés).

function miFuncion() {

var variableLocal = 5;

funcionB();

function funcionA() {

return variableLocal;

}

function funcionB() {

console.log(funcionA()); // Esto da 5

}

}

miFuncion();

Funciones autoejecutables

Bueno, pues aunque no es exactamente código encapsulado, ya sabemos una manera sencilla de pseudo-emular esta forma de desarrollar código, fundamental a la que hay unas cuantas líneas. Basta con declarar las cosas dentro de un par de llaves, ya sea las que pertenecen a una función o a un objeto.

En este sentido, es muy recomendable «encapsular» nuestros plugins o utilidades en funciones autoejecutables, es decir, las que se ponen en marcha en cuanto se cargan. Su sintaxis básica es muy sencilla:

(function () {

// Aquí todo el tinglado

})();

Pero tal y como aprendí de un vídeo-tutorial de Pablo Rigazzi, que es un crack, se puede hacer aún más chula. El segundo juego de paréntesis es el que llama a la función y se puede utilizar para enviar parámetros que se recogen en el primer par de paréntesis.

(function(recogeVariable){

console.log(recogeVariable);

})("Hola Saturno");

Además de servir para enviarle parámetros que se puedan necesitar para algo, ahí podemos hacer esto:

(function(window, undefined){

// Tingladete;

})(window);

con lo que conseguimos dos cosas en las que no voy a profundizar ahora:

  • optimizar el rendimiento al enviarle el objeto window, por lo que no debe subir de nivel para utilizarlo.
  • evitar que se pueda reescribir undefined.

En cualquier caso, esto no es importante ahora, sino el ver cómo podemos encapsular el código en funciones autoejecutables. Y para emplear variables globales, basta con declararlas fuera de cualquier llave.

var miVariableGlobal = 5;

(function(window, undefined){

console.log(miVariableGlobal) // Esto da 5

var miVariableLocal = 5;

})(window);

(function(window, undefined){

console.log(miVariableLocal) // Esto da error, undefined

})(window);

hoisting

Para ir terminando esta entrada vamos a ver un comportamiento extraño de JavaScript que alguna vez puede provocarnos un lío de cuidado si no lo controlamos.

Como es sabido, las variables en JavaScript se declaran anteponiendo la palabra reservada var.

var miVariable;

En teoría, se podría prescindir de la palabra var y declararlas directamente...

miVariable;

...pero, en realidad, lo que hace JavaScript es sobreentender que justo antes se ha declarado con var, lo cual puede provocar extrañas consecuencias cuando se junta con el «hoisting», como veremos dentro de un momento.

var miVariable; // Esto se sobreentiende

miVariable;

También es sabido que no se puede emplear una variable cuyo valor no haya sido definido, es decir, que sea undefined, pues daría error. Claro: ¿Cuánto es 5 más undefined? Pues imposible de saberlo.

Y ahora fijémonos en esto. Si escribimos este código, por consola se muestra la variable sin problemas, pues ha sido declarada e inicializada por encima del par de llaves de la función anónima autoejecutable.

var miVariableGlobal = "Hola Mundo";

(function(){

console.log(miVariableGlobal); // Hola Mundo

})();

Y, claro está, podemos cambiarle el valor sin problemas...

(function(){

console.log(miVariableGlobal); // Hola Mundo

miVariableGlobal = "Hola Júpiter";

console.log(miVariableGlobal); // Hola Júpiter

})();

Sin embargo, si volvemos a anteponer la palabra var, que solo hay que emplearla cuando se declaran por primera vez, el motor de JavaScript se vuelve tarumba con un undefined. ¿Por qué?

var miVariableGlobal = "Hola Mundo";

(function(){

console.log(miVariableGlobal); // Undefined

var miVariableGlobal = "Hola Júpiter";

console.log(miVariableGlobal); // Hola Júpiter

})();

Esto es un por un comportamiento que se denomina hoisted, el cual consiste en que JavaScript sube de forma automática -e invisible- todas las variables cuando se declaran al principio del par de llaves más cercano. Es decir, en el caso anterior es como si hubiéramos escrito esto:

var miVariableGlobal = "Hola Mundo";

(function(){

var miVariableGlobal;

console.log(miVariableGlobal); // Undefined

var miVariableGlobal = "Hola Júpiter";

console.log(miVariableGlobal); // Hola Júpiter

})();

Por lo tanto, como tratamos de emplear la variable antes de haberla inicializado, da error. Para evitarlo —y porque así el código gana en legibilidad— lo mejor es acostumbrarse a declarar las variables siempre al principio de la función.

Bueno, de momento vamos a dejarlo aquí y ya en la próxima entrada comenzaremos a ver las posibilidades que ofrece el prototipado de objetos.

|| Tags: , ,

valoración de los lectores sobre JavaScript OO: 2. Closures

  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • 4.6 sobre 5 (11 votos)

¿Te ha parecido útil o interesante esta entrada?
dormido, valoración 1 nadapensativo, valoración 2 un poco sonrisa, valoración 3 a medias guiño, valoración 4 bastante aplauso, valoración 5 mucho

Tú opinión es muy importante, gracias por compartirla!

Los comentarios están cerrados.