Definiendo conceptos: Closure y Scope en JavaScript
Qué es el Scope
El scope es el alcance de una variable, puede ser de dos tipos, global y local. Una variable cuyo scope es global se puede acceder desde cualquier parte del código, una local solo desde la función que la contiene. Ejemplo:
var a = 1;
function global() {
console.log(a);
}
global();
console.log(a);
En ese caso a es una variable global ya que podemos acceder tanto fuera como dentro de una función debido a haberla definido fuera de cualquier función.
function local() {
var a = 2;
console.log(a);
}
local();
console.log(a);
En este otro caso, la variable a es local ya que la definimos dentro de la función local(), esto quiere decir que solo podemos acceder a ella dentro dicha función, cuando ejecutamos local() te muestra correctamente 2, mientras que si haces console.log(a) te va a dar error porque a no esta definida, para el scope global esa variable no existe.
Esto te permite a vos decidir si querés una variable solo para X función o incluso si queres que una variable cambie su valor dentro de una función.
ECMAScript 2015/6
En Julio de 2015 se incorporó al estándar ECMAScript (el cual sirve de base para JavaScript) nuevas formas de definir variables con un scope diferente usando la palabra let en vez de var.
Este nuevo scope se lo conoce como scope de bloque. A diferencia del scope tradicional por función este scope esta limitado al bloque de código donde fue definida la variable.
Un bloque de código es el que se encuentra entre llaves o curly braces ({ y }), esto además de incluír las funciones incluye ciclos y condiciones, esto quiere decir que una variable definida con let puede solo existir en el scope de un ciclo o una condición por ejemplo:
for (let i = 0; i < 10; i++) {
console.log(i);
}
console.log(i); // error
En este ejemplo la variable i definida con let solo exíste dentro del cíclo por lo que el console.log dentro del ciclo imprime correctamente el valor de i mientras que el console.log fuera del ciclo daría un error porque la variable i no esta definida.
Qué es un Closure
Un closure es una función que es libre de variables, esto quiere decir que las variables de la función padre funcionan, pero el closure no tiene variables propias. Ejemplo:
function padre() {
var a = 1;
function closure() {
console.log(a);
}
closure();
}
padre();
La función padre() crea una variable local y una función closure(). Esta función interna es un closure y solo esta disponible dentro de padre(). A diferencia de padre() esta función no tiene variables locales y usa las declaradas dentro de padre().
Ahora considera este otro ejemplo:
function crearFuncion() {
var a = 1;
function closure() {
console.log(a);
}
return closure;
}
var miFuncion = crearFuncion();
miFuncion();
Este código hace lo mismo que el anterior. La diferencia es que la función closure es regresada por crearFuncion() sin haberse ejecutado. Normalmente una variable local solo existe mientras se ejecuta dicha función y luego no es más accesible, pero en este caso eso no se cumple. Eso es porque miFuncion() se convirtió en un closure, un objeto que combina una función y el contexto donde fue creada, el contexto consiste en cualquier variable que estaba en su mismo nivel de scope (las que pertenecen a crearFuncion).
En este caso miFuncion() se convirtió en un closure que incorpora la función closure() y el valor 1 almacenado en a cuando la función fue creada. Acá hay un ejemplo mejor:
function crearSuma(a) {
return funcion(b) {
return a + b
}
}
var sumar5 = crearSuma(5);
var sumar10 = crearSuma(10);
console.log(sumar5(15));
console.log(sumar10(15));
En este ejemplo la función crearSuma recibe un argumento a y retorna una función. La función que retorna recibe un argumento b y retorna la suma de a y b. Eso convierte a crearSuma() en una función capaz de crear otras funciones. En el ejemplo se usa crearSuma() para crear dos funciones, la primera para sumar 5 a una valor b y la segunda suma 10.
Ejemplo más práctico:
function changeSize(a) {
return function() {
document.body.style.fontSize = a + 'px';
}
}
var size12 = changeSize(12);
var size14 = changeSize(14);
var size16 = changeSize(16);
Esta tres funciones size12, size14 y size16 permiten cambiar el tamaño de fuente al valor indicado al momento de crear el closure. Luego podemos asignarlas al evento click de algún link y permitir de esta forma que se cambie facilmente el tamaño de fuente de nuestro sitio.