Guía de cierres de JavaScript

     

    Introducción

    Los cierres son un concepto algo abstracto del lenguaje JavaScript y se cuelan en el lado del compilador de la programación. Sin embargo, comprender cómo JavaScript interpreta las funciones, las funciones anidadas, los ámbitos y los entornos léxicos es imprescindible para aprovechar todo su potencial.

    En este artículo, intentaremos desmitificar dichos conceptos y proporcionar una guía sencilla para los cierres de JavaScript.

    ¿Qué es un cierre?

    Primero, echemos un vistazo a la definición oficial de MDN de cierre:

    Un cierre es la combinación de una función agrupada (encerrada) con referencias a su estado circundante (el entorno léxico). En otras palabras, un cierre le da acceso al alcance de una función externa desde una función interna.

    En términos más simples, un cierre es una función que tiene acceso al alcance de una función externa. Para entender esto, echemos un vistazo a cómo funcionan los ámbitos en JavaScript.

    Alcance en JavaScript

    Alcance determina qué variables son visibles o pueden ser referenciadas en un contexto dado. El alcance se divide ampliamente en dos tipos: Alcance global y Alcance local:

    • Alcance global – variables definidas fuera de una función. Se puede acceder a las variables en este ámbito y modificarlas desde cualquier lugar del programa, de ahí el nombre “global”.
    • Alcance local – variables definidas dentro de una función. Estas variables son específicas de la función en la que se definen, por lo que se denominan “locales”.

    Echemos un vistazo a una variable global y local en JavaScript:

    let name = "Joe";
    
    function hello(){
        let message = "Hello";
        console.log(message + " " +name);
    }
    

    En el ejemplo anterior, el alcance de name es global, es decir, se puede acceder a él desde cualquier lugar. Por otra parte, message se define dentro de una función, su alcance es local al hello() función.

    JavaScript utiliza el ámbito léxico cuando se trata de ámbitos de función. Lo que significa que el alcance de una variable se define por la posición de su definición en el código fuente. Esto nos permite hacer referencia a variables globales dentro de ámbitos más pequeños. Una variable local puede usar una variable global, pero viceversa no es posible.

    En

    function outer(){
        let x = 10;
        
        function inner() {
            let y = 20;
            console.log(x);
        }
        
        inner();
        console.log(y)
    }
    
    outer();
    

    Este código da como resultado:

    10
    error: Uncaught ReferenceError: y is not defined
    

    los inner() función puede hacer referencia x ya que está definido en el outer() función. sin embargo, el console.log(y) declaración en el outer() la función no puede hacer referencia al y variable porque está definida en la inner() alcance de la función.

    Además, en este escenario:

    let x = 10;
    
    function func1(){
       console.log(x);
    }
    
    function func2() {
      let x = 20;
      func1();
    }
    
    func2();
    

    La salida será:

    10
    

    Cuando llamamos func1() desde dentro func2(), tenemos una variable de ámbito local x. Sin embargo, esta variable es totalmente irrelevante para func1() ya que no es accesible en func1().

    Así, func1() comprueba si hay una variable global con ese identificador disponible y lo usa, lo que da como resultado el valor de 10.

    Cierres bajo el capó

    Un cierre es una función que tiene acceso a las variables de sus padres incluso después de que la función externa haya regresado. En otras palabras, un cierre tiene tres ámbitos:

    • Alcance local: acceso a variables en su propio alcance
    • Alcance de la función principal: acceso a las variables dentro de su principal
    • Alcance global: acceso a variables globales

    Echemos un vistazo a un cierre en el trabajo, haciendo una función que devuelva otra función:

    function outer() {
        let x = 3
        return function inner(y) {
            return x*y
        }
    }
    
    let multiplyByThree = outer();
    
    console.log(multiplyByThree(2));
    

    Esto resulta en:

    6
    

    Si hacemos un:

    console.log(multiplyByThree);
    

    Somos recibidos con:

    function inner(y) { return x * y; }
    

    Repasemos el código paso a paso para ver qué está sucediendo bajo el capó:

    • los outer() La función se define en el ámbito global.
    • outer() se invoca y devuelve una función que se asigna a multiplyByThree.
    • Se crea un nuevo contexto de ejecución para outer().
      • Variable x está configurado en 3.
    • Devuelve una función llamada inner().
    • La referencia a inner() está asignado a multiplyByThree.
    • Cuando la función externa finaliza la ejecución, se eliminan todas las variables dentro de su alcance.
    • Resultado de la llamada a la función multiplyByThree(2) se registra en la consola.
    • inner()se invoca con 2 como argumento. Entonces, y se establece en 2.
    • Como inner() conserva la cadena de alcance de su función principal, en el momento de la ejecución todavía tendrá acceso al valor de x.
    • Vuelve 6 que se registra en la consola.

    En conclusión, incluso después de la exterior la función deja de existir, la interior La función tiene acceso a las variables definidas en el alcance de la exterior función.

    Visualización de cierres

    Los cierres se pueden visualizar a través de la consola del desarrollador:

    function outer() {
        let x = 3
        return function inner(y) {
            return x*y
        }
    }
    
    let multiplyByThree = outside();
    console.dir(multiplyByThree);
    

    Al ejecutar el código anterior en la consola del desarrollador, podemos ver que tenemos acceso al contexto de inner(y). Tras una inspección más cercana, podemos ver que parte de su contexto es un [[Scopes]] array, que contiene los tres ámbitos de los que estábamos hablando.

    Y he aquí, la matriz de alcances contiene el alcance de su función principal, que contiene x = 3:

    Casos de uso común

    Los cierres son útiles porque nos ayudan a agrupar datos con funciones que operan en esos datos. Esto podría sonarles a algunos de ustedes que están familiarizados con la programación orientada a objetos (OOP). Como resultado, podemos usar cierres en cualquier lugar donde podamos usar un objeto.

    Otro caso de uso importante de los cierres es cuando necesitamos que nuestras variables sean privadas, ya que las variables definidas en el alcance de un cierre están fuera de los límites de las funciones fuera de él. Al mismo tiempo, los cierres tienen acceso a variables en su cadena de alcance.

    Veamos el siguiente ejemplo para entender esto mejor:

    const balance = (function() {
        let privateBalance = 0;
    
        return {
            increment: function(value){
                privateBalance += value;
                return privateBalance;
            },
            decrement: function(value){
                privateBalance -= value;
                return privateBalance;
            },
            show: function(){
                return privateBalance;
            }
        }
    })()
    
    console.log(balance.show()); // 0
    console.log(balance.increment(500)); // 500
    console.log(balance.decrement(200)); // 300
    

    En este ejemplo, hemos definido una variable constante balance y configúrelo como el valor de retorno de nuestra función anónima. Darse cuenta de privateBalance solo se puede cambiar llamando a los métodos en balance.

    Conclusión

    Aunque los cierres son un concepto bastante especializado en JavaScript, son una herramienta importante en un buen conjunto de herramientas para desarrolladores de JavaScript. Se pueden utilizar para implementar con elegancia soluciones que de otro modo serían una tarea difícil.

    En este artículo, primero hemos aprendido un poco sobre los ámbitos y cómo se implementan en JavaScript. Luego utilizamos este conocimiento para comprender cómo funcionan los cierres debajo del capó y cómo usarlos.

     

    Etiquetas:

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *