7654 votos

¿Cómo funcionan los closures en JavaScript?

Como el viejo Albert Einstein dijo:

Si no se lo puede explicar a un niño de seis años de edad, usted no lo entiende realmente.

Bien, he tratado de explicar los closures de JavaScript a un amigo de 27 años de edad, y he fracasado totalmente.

¿Cómo lo explicarías a alguien con un conocimiento de los conceptos que conforman los closures (por ejemplo. funciones, variables y similares), pero no entiende los closures en sí mismos?

He visto el Esquema de ejemplo que se da en Stack Overflow, y no sirvió de nada.

3850voto

Ali Puntos 553

Cada vez que vea la palabra clave de función dentro de otra función, el interior de la función tenga acceso a variables en el exterior de la función.

function foo(x) {
  var tmp = 3;
  function bar(y) {
    alert(x + y + (++tmp)); // will alert 16
  }
  bar(10);
}
foo(2);

Este siempre alerta de 16, porque bar puede acceder a las x que se define como un argumento a foo, y también se puede acceder a tmp de foo.

Que es un closure. Una función no tiene que regresar para ser llamado un closure. El simple acceso a variables fuera de su inmediata ámbito léxico crea un closure.

function foo(x) {
  var tmp = 3;
  return function (y) {
    alert(x + y + (++tmp)); // will also alert 16
  }
}
var bar = foo(2); // bar is now a closure.
bar(10);

La función anterior también alerta de 16, porque bar todavía puede referirse a x y tmp, aunque no es directamente dentro del ámbito de aplicación.

Sin embargo, dado tmp todavía cuelga alrededor de adentro bar's de cierre, también se incrementa. Será incrementado cada vez que usted llame bar.

El ejemplo más simple de un cierre a este:

var a = 10;
function test() {
  console.log(a); // will output 10
  console.log(b); // will output 6
}
var b = 6;
test();

Cuando una función de JavaScript que se invoca, un nuevo contexto de ejecución se crea. Junto con los argumentos de la función y el objeto primario, en este contexto de ejecución también recibe todas las variables declaradas fuera de ella (en el ejemplo anterior, 'a' y 'b').

Es posible crear más de un cierre de función, devolviendo una lista de ellos o por ajuste a las variables globales. Todos estos se refieren a la mismax y el mismo tmp, no hacer sus propias copias.

Aquí el número x es un número literal. Como con otros literales en JavaScript, cuando foo es llamado, el número x es copiado en foo como argumento x.

Por otro lado, JavaScript usa siempre referencias a la hora de tratar con Objetos. Si decir, que llamó foo con un Objeto, el cierre se devoluciones de referencia que el Objeto original!

function foo(x) {
  var tmp = 3;
  return function (y) {
    alert(x + y + tmp);
    x.memb = x.memb ? x.memb + 1 : 1;
    alert(x.memb);
  }
}
var age = new Number(2);
var bar = foo(age); // bar is now a closure referencing age.
bar(10);

Como era de esperar, cada llamada a bar(10) incrementará x.memb. Lo que no se puede esperar, es que x es simplemente hace referencia al mismo objeto como age variable! Después de un par de llamadas a bar, age.memb 2! Esta referencia es la base para fugas de memoria con objetos HTML.

2285voto

Jacob Swartwood Puntos 3994

Soy un gran fan de la analogía y la metáfora a la hora de explicar conceptos difíciles, así que voy a probar mi mano con una historia.

Érase una vez:

Había una princesa...

function princess() {

Ella vivía en un mundo maravilloso lleno de aventuras. Ella conoció a su Príncipe Encantador, se montó alrededor de su mundo en un unicornio, lucharon contra los dragones, encuentran hablando de animales, y muchas otras cosas fantásticas.

    var adventures = [];

    function princeCharming() { /* ... */ }

    var unicorn = { /* ... */ },
        dragons = [ /* ... */ ],
        squirrel = "Hello!";

Pero ella siempre tiene que volver a su mundo aburrido de las tareas domésticas y de adultos.

    return {

Y a menudo hablaba de su última aventura increíble como una princesa.

        story: function() {
            return adventures[adventures.length - 1];
        }
    };
}

Pero todos los que iban a ver es una niña...

var littleGirl = princess();

...contar historias acerca de la magia y la fantasía.

littleGirl.story();

Y aunque los adultos sabían de auténticas princesas, la que nunca creo en los unicornios o dragones ya que nunca pudieron verlos. Los adultos dijo que sólo existía dentro de la pequeña niña de la imaginación.

Pero nosotros sabemos la verdad; que la niña con la princesa en el interior...

...es realmente una princesa con una niña dentro.

705voto

dlaliberte Puntos 1947

Tomando la cuestión en serio, debemos averiguar lo que es un típico niño de 6 años es capaz de cognitivamente, aunque es cierto que, quien esté interesado en JavaScript no es tan típico.

En Desarrollo de la primera Infancia: de 5 a 7 Años dice:

Su hijo será capaz de seguir las indicaciones de dos pasos. Por ejemplo, si le dice a su hijo : "Ve a la cocina y me dio una bolsa de basura" que será capaz de recordar esa dirección.

Podemos utilizar este ejemplo para explicar los cierres, de la siguiente manera:

La cocina es un cierre que tiene una variable local, llamado trashBags. Hay una función dentro de la cocina llamado getTrashBag que se pone una bolsa de basura y se la devuelve.

Podemos código en JavaScript como este:

function makeKitchen () {
  var trashBags = ['A', 'B', 'C']; // only 3 at first

  return {
    getTrashBag: function() {
      return trashBags.pop();
    }
  };
}

var kitchen = makeKitchen();

kitchen.getTrashBag(); // returns trash bag C
kitchen.getTrashBag(); // returns trash bag B
kitchen.getTrashBag(); // returns trash bag A

Más puntos que explican por qué los cierres son interesantes:

  • Cada vez makeKitchen() es llamado, un nuevo cierre se crea con sus propios trashBags.
  • El trashBags variable es local en el interior de cada cocina y no es accesible desde fuera, pero el interior de la función en el getTrashBag propiedad tiene acceso a él.
  • Cada llamada a la función crea un cierre, pero no habría ninguna necesidad de mantener el cierre de alrededor de menos de un interno de la función, que tiene acceso al interior de la clausura, se puede llamar desde fuera de la clausura. Devolver el objeto con el getTrashBag función hace aqui.

455voto

Konrad Rudolph Puntos 231505

Los cierres son difíciles de explicar porque son usados para hacer algún comportamiento en el trabajo que todo el mundo espera intuitivamente a funcionar de todos modos. Me parece la mejor manera de explicar (y de la manera en que yo aprendí lo que hacen) es imaginar la situación sin ellos:

var bind = function(x) {
    return function(y) { return x + y; };
}

var plus5 = bind(5);
alert(plus5(3));

¿Qué pasaría aquí si JavaScript no sé cierres? Basta con sustituir la llamada en la última línea de su cuerpo de método (que es básicamente lo que la función de llamadas) y se obtiene:

alert(x + 3);

Ahora, ¿dónde está la definición de x? No hemos de definir en el ámbito actual. La única solución es dejar que plus5 llevar a su alcance (o más bien, su padre el alcance). De esta manera, x está bien definido y es enlazado con el valor 5.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X