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 tiene su propio contexto, esto quiere decir que las variables de la función padre funcionan dentro del closure, pero no se pueden acceder desde afuera. 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().

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() 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);

Estas 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 botón y permitir de esta forma que se cambie facilmente el tamaño de fuente de nuestro sitio.