jueves, 15 de octubre de 2020

Javascript: Ejemplo de Closure

Ejemplo

function inicial() {
    // var1 definida e inicializada para el scope de inicial()
    let var1 = 1;        

    function interna() {
        // var2 definida e inicializada para el scope de interna()
        let var2 = 2;    
        console.log( var1, var2 );
    }

    // inicial() retorna función. !OJO¡ totalmente distinto a
    // 'return interna()' que devolvería el retorno de interna()
    return interna;      
}

// a externa le asignamos el retorno de inicial(), que es
// la referencia a la función interna
let externa = inicial();             

// Resulta en consola 1 2 -- Accede al contenido de variables que
// están fuera de su scope.
// Estamos ante una !!!CLOSURE!!!, bueno dos: var1 y var2
externa();

Definición

Se produce una Closure cuando una función es capaz de recordar y acceder a su lexical scope mientras se ejecuta fuera de él.

Explicación

En este ejemplo a la variable externa se le asigna la función inicial(). Esta función declara dentro de su scope las variables var1 y var2.
 
Cuando en la última línea de código se ejecuta externa(), realmente la función que se está ejecutando es interna() que es el retorno de ejecutar inicial() en la sentencia let externa = inicial();. Esta  función interna() al ejecutarse hace uso de variables que están definidas e inicializadas en el scope de inicial() , pero se está ejecutando desde fuera del scope de inicial(). Esto es la closure.

Conclusión

Gracias a las closures, hay variables que pueden se usadas fuera de su scope. Eso sí, protegidas y manejadas por las funciones dentro de las que están declaradas. 

Bibliografía

'You Don't Know JS: Scope & Closures' de Kyle Simpson publicado por 'Reilly Media, Inc en Marzo 2014

domingo, 11 de octubre de 2020

Javascript: Ejemplo de expresiones regulares.

Esto es una ayuda para quien tenga que construir una expresión regular y no sepa como hacerlo.
Los ejemplos están desarrollados con el método test de regexp. El método "test" devuelve true cuando hay
coincidencia del patrón dentro de la cadena analizada y false cuando no.
Los ejemplos tienen la estructura:

    let patr = /patron/;  
    console.log (Descripción prueba, patr.test(cadena a analizar));

Ejemplos 

// Expresión regular sencilla>
let patr = /abc/; // contiene la subcadena 'abc'
console.log ("Creación expresión: true", patr.test("abcdefghi"));
console.log ("Creación expresión: false", patr.test("acdefghi"));

// Caracteres: Letras, numeros, espacios, etc
patr = /[abc]/; // contiene a y/o b y/o c
console.log ("Caracteres 1: true", patr.test("acdefghi"));
patr = /[a-c]/;
console.log ("Caracteres 2: true", patr.test("acdefghi"));
patr = /[abc]/;
console.log ("Caracteres 3: false", patr.test("defghi"));
patr = /[a-zA-Z0-9]/; //contiene alguna minúscula, mayúscula y/o
// un número de 0 a 9
console.log ("Caracteres 4: true", patr.test("defghi Amarillo"));
console.log ("Caracteres 5: true", patr.test("defghi98-Amarillo"));
console.log ("Caracteres 6: false", patr.test("!$%&/()="));
patr = /[^0-9]/; //negación, no contiene solo números del 0 al 9
console.log ("Caracteres 7: true", patr.test("98 y otras cosas"));
console.log ("Caracteres 8: false", patr.test("98"));

// Meta-caracteres (Ver tablas de meta-caracteres en readme)
patr = /\s/; // contiene caracter no visible en print:
// (tabulador, espacio, salto linea, enter, form feed)
console.log ("Meta-caracteres 1: true", patr.test("una   línea"));
patr = /\d/; // contiene dígitos del 0 al 9. Idéntico [0-9]
console.log ("Meta-caracteres 2: true", patr.test("Un numero 88"));
console.log ("Meta-caracteres 3: false", patr.test("No hay número"));

// Caracteres de comienzo y fin
patr = /^auto$/; // contiene exactamente auto
console.log("Comienzo y fin 1: true", patr.test("auto"));
console.log("Comienzo y fin 2: false", patr.test("automático"));
console.log("Comienzo y fin 3: false", patr.test("Qué bonito auto"));

patr = /auto$/; // acaba en auto
console.log("Comienzo y fin 4: true", patr.test("Qué bonito auto"));
console.log("Comienzo y fin 5: false", patr.test("Bonito auto rojo"));

// Boundaries. Definen límites de palabra.
patr = /\bco/; // La palabra empieza por "co"
console.log('Ejemplo de boundary 1 true ', patr.test("coche"));
console.log('Ejemplo de boundary 2 true ', patr.test("iré en coche"));
console.log('Ejemplo de boundary 3 false ', patr.test("acometida"));

// Caracter "." indica cualquier caracter
patr = /.......uto/;  // 7 caracteres cualesquiera y la cadena uto
console.log('Cualquier caracter 1 false', patr.test("auto"));
console.log('Cualquier caracter 2 false', patr.test("automatic"));
console.log('Cualquier caracter 3 true', patr.test("distributor"));

// Operadores de repetición: +, * y ?
patr = /[a-f]+/; // contiene una a o varias, o una b, .. o una f
console.log('Operador + 1: true', patr.test("Hace 10 días que no le veo"));
console.log('Operador + 2: true', patr.test("castaña"));
console.log('Operador + 3: false', patr.test("Auto"));
console.log('Operador + 4: false', patr.test("42"));

patr = /[a-f]*/; // contiene ninguna a o varias, o ninguna b, .. o una f
console.log('Operador * 1: true', patr.test("Hace 10 días le vi"));
console.log('Operador * 2: true', patr.test("castaña"));
console.log('Operador * 3: true', patr.test("Auto"));
console.log('Operador * 4: true', patr.test("42"));

patr =/^a.*/; //comienza por a minúscula
console.log('Operador * 5: false', patr.test("amarillo"));
console.log('Operador * 5: false', patr.test("rojo"));

patr = /.a?../; // En la segunda posición hay una a o ninguna
console.log('Operador ? 1: true', patr.test("casa"));
console.log('Operador ? 2: true', patr.test("sal"));
console.log('Operador ? 3: true', patr.test("box"));
console.log('Operador ? 4: false', patr.test("42"));

patr = /.al{2}../; // hay a en la segunda y l se repite 2 veces en la // tercera y cuarta posición
console.log('Operador {num} 1: true', patr.test("sallgo"));
console.log('Operador {num} 2: false', patr.test("palo"));

patr = /.a{1,5}../; // desde la segunda posición a se repite de 1 a 5 // veces
console.log('Operador {min,max} 1: false', patr.test("polo"));
console.log('Operador {min,max} 2: true', patr.test("vaale"));
console.log('Operador {min,max} 2: true', patr.test("vaaaaale"));

// Grupos
patr = /(ab)+/; // se repite la cadena 'ab' una vez o varias. Entre // paréntesis se especifican grupos
console.log('Ejemplo de grupo 1: true ',patr.test("abc"));
console.log('Ejemplo de grupo 2: true ',patr.test("abcabc"));
console.log('Ejemplo de grupo 3: false',patr.test("aacbb"));

//Referencias
patr = /(ab)+...\1/;
// se repite la cadena 'ab' una vez o varias después tres caracteres // cualesquiera y finalmente 'ab' una o varias veces.
// \1 es una referencia al grupo 1, en este caso (ab).
console.log('Ejemplo de referencia 1: true ',patr.test("abcccab"));
console.log('Ejemplo de referencia 2: false ',patr.test("abccc"));
console.log('Ejemplo de referencia 3: true',patr.test("abacbab")); 
 

 

 

 

domingo, 4 de octubre de 2020

Javascript: Ejemplo de función usada como parámetro (callback).

Las funciones son ciudadanos de primera clase en Javascript. Cuando se dice primera clase, es porque las funciones se tratan como cualquier otro tipo, ya sea primitivo o resultado de instanciar una clase creada por el desarrollador. Es decir, una función se puede utilizar igual que los otros tipos,  almacenándola en una array, utilizándola como parámetro de otra función, almacenándola  en una variable, etc.

Se muestra a continuación un ejemplo que hace uso de una función como parámetro de otra. Este ejemplo se muestra la misma funcionalidad codificada con 3 sintaxis distintas: funciones clásicas, funciones asignadas a una variable y funciones arrow asignadas a una variable.

1. Funciones clásicas

    function hi (name, lastName) {return `Hi ${name} ${lastName}`;}
    function bye (name, lastName) {return `Bye ${name} ${lastName}`;}
    function greeting (f) {
        const name ="pepe";
        const lastName = "suarez";
        return f(name, lastName);
    }
    console.log (greeting(hi));
    console.log (greeting(bye));

2.  Funciones asignadas a una variable

    const hi = function (name, lastName) {
        return `Hi ${name} ${lastName}`;
        }
    const bye =  function (name, lastName) {
        return `Bye ${name} ${lastName}`;
        }
    const greeting = function (f) {
        const name ="pepe";
        const lastName = "suarez";
        return f(name, lastName);
    }
    console.log (greeting(hi));
    console.log (greeting(bye));

3. Funciones arrow asignadas a una variable

    const hi = (name, lastName) => `Hi ${name} ${lastName}`;
    const bye = (name, lastName) => `Bye ${name} ${lastName}`;
    const greeting = (f) => {
        const name ="pepe";
        const lastName = "suarez";
        return f(name, lastName);
    }
    console.log (greeting(hi));
    console.log (greeting(bye));

 
 
Estos tres códigos son equivalentes, es decir si los ejecutamos obtenemos el mismo resultado:

    Hi pepe suarez
    Bye pepe suarez

Las funciones conocidas como callback, son funciones que se pasan como parámetros de otras funciones y que se ejecutan dentro de éstas. Es decir el ejemplo tiene  dos funciones hi y bye que son llamadas como callbacks desde la función greeting.

Esta posibilidad de llamar a funciones desde otras funciones que las han recibido como parámetros, acerca a javascript hacia la programación funcional. La programación funcional permite desarrollar código más estable, porque está menos acoplado y es más legible frente a la programación imperativa.

Si se supone que no se puede utilizar una función como parámetro. Una posible codificación para conseguir una funcionalidad idéntica a la mostrada podría ser:

    function hi (name, lastName) {return `Hi ${name} ${lastName}`;}
    function bye (name, lastName) {return `Bye ${name} ${lastName}`;}
    function greeting (greetingType) {
        const name ="pepe";
        const lastName = "suarez";
        if (greetingType=="hiType"){
          return hi(name, lastName);
        } else {
          return bye(name, lastName);
        }
    }
   console.log (greeting("hiType"));
   console.log (greeting("byeType"));

Este código tiene sabor a lo que se conoce como programación tipo imperativa.

Si fuese necesario incluir un nuevo tipo de saludo por ejemplo “Buenas tardes” los cambios a realizar serían en cada caso serían:

- En el caso “tipo funcional” tendríamos que añadir la función goodEvening(name, lastName) y una  llamada a la función greetings, es decir greetings(goodEvening). Ningún cambio más es necesrio.

- En el caso “tipo imperativo” tendríamos que añadir la función goodEvening(name, lastName), un  nuevo tipo de saludo greeting(“goodEveningType”) y modificar la función greeting para que el if considerase que pueden existir un nuevo tipo de saludo.

Conclusión.

La posibilidad de utilizar funciones como parámetros permite generar un código más orientado a la extensión que a la modificación. Esto es debido a que en el caso “tipo funcional” extendemos el comportamiento del sistema sin la necesidad de cambiar nada en la función greeting. La modificación es más costosa en el caso “tipo imperativo” incluye tener que modificar la propia función greeting.