Saltar al contenido

Aprenda a utilizar los cierres de JavaScript

Los cierres se utilizan sobre todo en las llamadas, como los tiempos de espera, los manejadores de eventos, etc., así como en los módulos.

Consideremos el siguiente código:

Aprenda a utilizar los cierres de JavaScript
Aprenda a utilizar los cierres de JavaScript
1234for(var i =0; i <5; i++){console.log(i);}

JavaScript

La salida de este código son números del 0 al 4, secuencialmente.

Cambiemos un poco:

12345678for(var i =0; i <5; i++){setTimeout(function(){console.log(i);},3000);}

JavaScript

La salida del código anterior será completamente diferente; el número 5 será registrado 5 veces.

Analicemos este código. La función, que se pasa como argumento a la función setTimeout, se ejecutará 3s después de la primera iteración del bucle. Por lo tanto, el valor de la variable i, que está en su ámbito exterior (esta función es también un cierre), se convertirá en 5 antes de su ejecución porque las 5 iteraciones se harán ciertamente dentro de esos 3 segundos. Esa función se ejecutará 5 veces, 1 vez por cada iteración, y registrará 5 cinco veces.

En otras palabras, para cuando el Timeout termine, el valor de i será 5, y este valor es el que se registrará.

Usando cierres para arreglar el asunto

Para obtener la salida deseada, necesitaremos añadir un cierre más, por lo que nuestro código debería verse así:

12345678910for(var i =0; i <5; i++){setTimeout((functionouter(i){returnfunctioninner(){console.log(i);// 0 1 2 3 4}})(i),3000);}

JavaScript

Así que, ahora nuestro código contiene 2 cierres, exterior e interior, que ambos tienen acceso a la variable i, pero hay una ligera diferencia entre ellos. El ámbito externo de la función externa se crea una vez y el ámbito externo de la función interna se crea 5 veces, para cada iteración por separado. El ámbito exterior de la función externa contiene la variable i, creada en el bucle for, mientras que el ámbito exterior de la función interna contiene esta variable, un argumento de la función externa. Por lo tanto, se trata de dos variables diferentes con el mismo nombre.

En la primera iteración el valor de la variable i es 0. Hemos invocado la función externa inmediatamente con el parámetro 0 y nos ha devuelto la función interna. Así, la función interna se proporcionó realmente como un argumento a la función setTimeout en lugar de la función externa. Por lo tanto, la función interna será llamada después de 3s y tendrá acceso a su propio ámbito externo. El valor de la variable i en su ámbito es 0, por lo que la función interna registrará 0 en el momento de su ejecución.

Lo mismo ocurrirá en la segunda iteración, pero esta vez la variable i será 1, y así sucesivamente. Finalmente, obtendremos la salida deseada en la consola.

Una forma más fácil

Sin embargo, hay una solución más simple para este problema. El ECMAScript 2015 (o ES6) introdujo algunas nuevas características muy útiles y algunas de ellas son let y const palabras clave, que crean variables de alcance de bloque. Si simplemente reemplazamos la palabra clave var por let en este ejemplo, obtendremos la salida completamente diferente:

12345678for(let i =0; i <5; i++){setTimeout(function(){console.log(i);// 0 1 2 3 4},3000);}

JavaScript

La palabra clave permitió crear un ámbito de bloque para la variable i, para cada iteración de bucle en particular. Por lo tanto, no tuvimos que poner la línea console.log(i) en un nuevo cierre para crear un nuevo ámbito para i, porque la palabra clave let lo hizo por nosotros.