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 *