duduromeroa.com

#Javascript, #DOMJs, #webinteractiva, #Interfaces, #duduromeroa, #Guayaquil

Javascript

Programación para web (3/8): tipos de funciones, bloques de funciones, ámbitos; condicionales.


George Póyla, matemático. Resolver problemas de diseño. www.duduromeroa.com

Por Eduardo J. Romero Andrade
| Guayaquil

Contacto duduromeroa@gmail.com



FUNCIONES

Una función de JS es similar a una granja que recibe productos desde el exterior, y usa bodegas para alojar esos productos que recibe o entrega. En JS la granja es la función, los contenedores los parámetros y los productos los argumentos que recibe desde fuera.

Las funciones se declaran o se expresan. La declaración requiere un nombre identificador para la función, y solo la declaración de una función aplica para hoisting.

En cambio, la expresión de una función no requiere un nombre de función, pero sí requiere una variable para alojarla.

Ya sea declarada o expresada la función, el return de la función mantiene en espera un valor en memoria hasta que la función sea invocada:

// *** DECLARACIÓN DE FUNCIÓN
function calculo(num) {
return num * num;
};
     // Se agrega argumentos y se invoca
     console.log( calculo(2,3) );

// *** EXPRESIÓN DE FUNCIÓN
var cuadrado = function(num) {
return num * num;
};

     /* Se invoca la VARIABLE */
      console.log(cuadrado(3));
      //-> 9

Para Haverbeke[1] "Las funciones están –conceptualmente– en la cima de su alcance o ámbito" gracias al hoisting. Eso permite que el sistema lea código "sin preocuparnos por declarar todas las funciones antes de que sean utilizadas".

En otras palabras, JS permite 'llamar' a las funciones antes que estén listas. En el ejemplo de abajo se activará (de forma invisible para nosotros) el hoisting o elevación de las declaraciones de funciones. Esto es, que el sistema las leerá como si esas declaraciones hubieran estan al inicio del código, antes de sus invocaciones.

En el código de abajo el numeral 1 es la invocación de dos funciones. Luego, los códigos con los numerales 1 y 2 son las declaraciones (las funciones ya creadas). Gracias al hoisting los numerales 1 y 2 pasarán al primer lugar por sobre sus invocaciones. Pero esa reubicación solo ocurrirá en el sistema. Para nosotros los códigos mantendrán la siguiente secuencia:

// 1) Aquí se llaman a dos funciones que aún no existen...
console.log("Guayaquil, " + frase1() + frase2());

// 2) Recién aquí se declara una de las funciones...
function frase2() { return " del Guayas."; }

// 3) Recién aquí se declara una de las funciones...
function frase1() { return "es la capital de la provincia"; }

//-> Guayaquil, es la capital de la provincia del Guayas.

En un sencillo ejemplo se muestra una función con dos parámetros '(animal, precio)' y su invocación. El signo \t es un tabulador de datos strings o caracteres. Como veremos, cada invocación de función creará un nuevo contexto (un nuevo hilo de datos) en cada llamado:

function granja(animal,precio){
console.log("Tenemos " + animal + " y cuesta \t$" + precio);
}
// Cada invocación permite nuevos datos
granja("gallina", 18);
granja("pollo", 22);
granja("chochos", 3.40);
/*
Cada llamado es un nuevo contexto
Tenemos gallina y cuesta  $18
Tenemos pollo y cuesta    $22
Tenemos chochos y cuesta  $1.4
*/

Funciones invocando funciones

Las funciones son tipos de datos. Por lo tanto, también podemos insertar funciones dentro de los parámetros de otra función para que operen datos y den resultados.

En Javascript

function primera(x){return x + 1;}

var y = 3;

console.log(primera(3));
// 4

let segunda = function(x){return x * x;};

// function( funcion(parametro) )
console.log( segunda(primera(y)) ); 

// 16

FUNCIONES DENTRO DE OBJETOS

En JS un objeto es un tipo de dato con propiedades y valores. Cada propiedad puede ser invocada desde fuera del objeto. Y una de esas propiedades puede ser también una función.

En JS un objeto es un tipo de dato con propiedades y valores. Cada propiedad puede ser invocada desde fuera del objeto. Y una de esas propiedades puede ser también una función que al ser invocada obtiene la primera propiedad del objeto al apuntar la clave this a la propiedad 1:

// Se define variable objeto
let obj = {
propiedad1: "Holi",
propiedad2: function cosa(a){ return this.propiedad1; }
};

console.log(obj.propiedad2() + " chicos!");
// Holi Amiwis

Recordar. Una función tiene:

Cada nuevo llamado de función es un nuevo contexto cuando ese llamado es guardado en una variable.

function granja(animal, precio){
return "Tenemos " + animal + " | Cuesta $" + precio;
}

/* Cada contexto en una variable */
var g1 = granja("pollo", 8);
var g2 = granja("gallina", 12);
var g3 = granja("chancho", 8.4);

// Se invoca variable
console.log(g1); // Tenemos pollo | Cuesta $8
console.log(g2); // Tenemos gallina | Cuesta $12
console.log(g3); // Tenemos chancho | Cuesta $8.4

Una función cuenta con un bloque de función –al igual que las estructuras de datos, como objetos o arreglos–.

El bloque de función agrupa todos los datos y operaciones que se activarán al invocar la función. Un ejemplo de bloque de función es el siguiente, donde esta vez la función solo tiene una salida de los datos desde console.log() –y no un return-.

function a(b){
// Bloque de función
var b = 10, c = b * b;
// Esto muestra el resultado
console.log( b + c );
}

// Se invoca función
a(10);
// 110

Obviar en una función el uso del return se justifica solo cuando se desea que una función ejecute una acción sencilla que no necesite recuperar el resultado al resto del código. Y cuando no se requiere detener la ejecución –como sí lo hace el return.

Otro ejemplo de función mostrándo el resultado en el navegador:

En HTML

<p id="elemento"></p>

En Javascript

// Crea función con parámetro
function operar(f) {
// Devolver valores y operadores
return "El número secreto es " + 
// Math.ceil() lleva resultado decimal al entero más alto
Math.ceil(
      (20/5) + 
      (f*10) + 
      Math.hypot(2,10));
}// Cierra función

/* Apunta al elemento ID 'elemento' e inserta 
el resultado de la función operar() */
document.getElementById("elemento").innerHTML = operar(1);

//-> El número secreto es 

/* El resultado se mostrará en el navegador 
con los estilos de tipografía y color dados en CSS */

En CSS

p{
margin: 0 auto;
text-align: center;
font-family: arial;
font-size: 95px;
color: orange;
}

Otra característica de las funciones es que "[pueden también ser] ejecutadas en la secuencia que son llamadas, pero no [siempre] en la secuencia que son definidas" (W3C, JavaScript Callbacks). Es decir, podemos también llamar a las funciones según el orden que necesitemos, y no será necesario ejecutarlas en el orden en que fueron creadas.

/* Recordar, sin 'return' 
los valores se mostrarán undefined */
function saludo1(a){ return a; };
function saludo2(b){ return b; };

console.log(saludo2("...segundito"));
console.log(saludo1("...primerito"));
//-> ...segundito
//-> ...primerito

FUNCIONES COMO VALORES DE VARIABLES

Una función puede ser usada como el valor de una variable y ser ejecutada al invocar el identificador de la variable.

function encenderLuz(){ 
console.log("Luz encendida");
}
encenderLuz(); 
// --> Luz encendida

var nuevoFoco = encenderLuz;
nuevoFoco();
// --> Luz encendida

Después de todo, "las funciones son objetos" que pueden ser usados como valores en otros tipos de datos (Zakas, 2014, p. 20), como en otra función. En el siguiente ejemplo tomado de Zakas (2014), la función sort() admite como argumento una función anónima que ejecuta una resta de ordenación entre los elementos de un arreglo.

Lo especial aquí es que la función insertada dentro de sort() será ejecutada con solo invocar al arreglo que vincula sort():

var num = [ 5, 15, 89, 1 ]; 

num.sort(function(a, b) {
return a - b;
});

console.log(num); 
// [ 1, 5, 15, 89 ]

FUNCIONES COMO PARÁMETROS o Callbacks -rellamado–

Una función también puede aceptar otra función como argumento. Un callback es una función pasada con argumento (W3C, JavaScript Callbacks).

En Javascript

// Función y parámetro
function saludar(nombre) {
// Muestra string y variable
console.log("Hola, " + nombre);
}

// Nueva función y parámetro con nuevo valor
function procesar(rellamado) {
// Variable y valor
var nombre = "ingresa tu nombre";
// Retorna 
return rellamado(nombre);
}

procesar(saludar);
//-> Hola, ingresa tu nombre

En otro ejemplo:

/* Función con dos parámetros */
function ejecutar(a, b) {
// Retorna cada parámetro PERO en forma de función 
return a() + b();
}

// Se crea nueva función
function uno() {
return 1;
}

// Se crea nueva función
function dos() {
return 2; }

/* Se invoca ejecutar(): en cada 
parámetro SOLO el identificador 
de cada función */
console.log(ejecutar(uno, dos));
// --> 3

// --- 

/* Otra forma de invocar ejecutar() es */
console.log(
// Función
ejecutar( 
//Argumento primero -separa coma-
function(){return 1;}, 
//Argumento segundo -sin coma-
function(){return 2;} )
);
// --> 3

Otro ejemplo de callback explica cómo son las relaciones entre función anónima, parámetros -los identificadores de los datos que alimentarán a la función–, argumentos -los datos ingresados desde fuera de la función– e invocación -el llamado a ejecutar la función–:

// *** EXPRESIÓN DE FUNCIÓN (función en variable)
// Declara variable alojando función anónima
let primeraFuncion = function(){
console.log("Leyendo primera función");
}

// ** DECLARACIÓN DE FUNCIÓN
// Declara función cuyo parámetro es otra función
function segundaFuncion(primeraFuncion){
// Invoca función
primeraFuncion();
console.log("Leyendo segunda función");
}

// INVOCA FUNCIÓN
// Obligado agregar el parámetro
segundaFuncion(primeraFuncion);

//--> Leyendo primera función
//--> Leyendo segunda función

Explicación de relaciones entre parámetros, argumentos e invocación en callbacks de Javascript. Duduromeroa, 2021.
Relaciones entre función anónima, argumentos, parámetros e invocación. Diagrama realizado en Miro.

En otro ejemplo se aplica la función setTimeout() para dar un tiempo de activación –dos segundos– para ejecutar función al referenciar el nombre de la variable que aloja la función:

let mostrarMensaje = function(){
console.log("Sorpresa!!!");
};

/* Función setTimeout() se alimenta del 
nombre de la variable y del tiempo de espera -2 segundos– */
setTimeout(mostrarMensaje, 2000);

// Sorpresa!!!
/* Este mensaje esperó 2 segundos hasta ser mostrado */

FUNCIONES ANÓNIMAS AUTOINVOCADAS (Immediately-Invoked Function Expressions o IIFE)

No requiere una función con un nombre identificador. Sus datos son locales y se activa apenas el código ya declarado es interpretado por el sistema. Cuando use return debe ser guardada en una variable para invocarla. Presenta un llamado directo entre paréntesis '( ... )'.

 /* Ejemplo de sintaxis de autoinvocada */
(function(param1, param2){
/* ... */})("arg1", "arg2");

// ----

/* Ejemplo A: Esta función muestra los 
datos mediante console.log() */
(function(a, nombre){ console.log(a+nombre);
// Ingreso de argumentos y autoinvocación 
})("Buen día, ", "Dudú.");
//-> Buen día, mi Lord Dudú.

//--------

/* Ejemplo B con expresión de función: Si necesitamos usar RETURN
 debemos guardar
la función autoinvocada en una variable para luego,
invocar la variable y mostrar los valores. */
const ejecutarFuncion = (function(a, nombre){ 
    return a + nombre;
})("Holi ", "Dudú.");

/* Ya que la función devuelve mediante return, 
es necesario invocarla desde la variable */
console.log(ejecutarFuncion);
//-> Holi Dudú. 

¿Una función siempre será anónima –sin su nombre identificador–? No. Solo podrá obviar ese nombre cuando sea una función inmediata (autoinvocada); o cuando una función sea almacenada en una variable (expresión de función); entonces allí sí la función puede ser declarada sin el nombre identificador, y solo será invocada desde el nombre de la variable:

/* Función anónima guardada en variable */
var funcionAnonima = function(){
console.log("Holi amiwis");
};
// Invocación
funcionAnonima();
//-> Holi amiwis


// Con return;
var funcionAnonima = function(){
return console.log("Holi amiwis");
};
funcionAnonima();
//-> Holi amiwis


// INCORRECTO
function(){
console.log("abc");
}
/*--> SyntaxError: Function 
statements require a function name */

¿Cuándo son útiles las funciones autoinvocadas? Para sus utilidades son:

EJEMPLO A)

/* NO es necesario invocarla para que se active. 
Se activará apenas termine de ser declarada. */
(function(a) {
console.log("Eres perla que " + a)
})("surgiste...");
// Eres perla que surgiste...

EJEMPLO B)

// Función padre o externa
function externa() {
// Se declara variable con un valor
let contador = 0;

// Se devuelve una función interna
return function() {
// La misma variable es activarFuncionada 
contador += 1;
// Retorna valor
return contador;
} 
}
/* ERROR al acceder desde 
fuera a la variable contador */
// console.log(contador);

/* RECORDAR: En un cierre de funciones, 
SOLO la función interna puede acceder 
a las variables de la función externa. */

// Se crea nuevo contexto para función
const activarFuncion = externa();

// Cada invocación da un activarFunciono. 
console.log(activarFuncion()); // 1
console.log(activarFuncion()); // 2
console.log(activarFuncion()); // 3

Otra implementación de función anónima sin parámetro y con parámetro:

// Anónima sin parámetro
(function () { 
console.log('hola...');
})();
//-> hola...

// Anónima con parámetro y argumento
( function ( cosita ) {  
console.log('Hola, ' + cosita + '!');
})('cosita');
//-> Hola, cosita!

Incluso al ser guardada en una variable, una función inmediata es activada tan pronto es declarada.

/* Nada 'invocó' a esta variable que guarda la función. 
Esta fue activada apenas fue
interpretada por el sistema. */
var guardar = (function (nombre, edad) {
var item = "* ";
    
console.log( `${item}` + "Soy " + nombre);
console.log(`${item}` + "Mi edad es " + edad);

// Llamado inmediato
// Ingresan dos argumentos
} ("Dudú", 100) );
// * Soy Dudú
// * Mi edad es 100

Y así mismo, permite usar funciones flecha para generar autoinvocación:

 // Función autoinvocada tipo flecha
((nombre, origen)=>{ 
console.log('Soy ' + nombre + ", y soy " + origen + ".");
})("Dudu", "Guayaco");
//-> Soy Dudu, y soy Guayaco.

O simplemente, autoinvocarse sin parámetro alguno o argumento:

(()=>{
console.log("¡Corred a las colinas!: Iron Maiden");
})();
/* ¡Corred a las colinas!: Iron Maiden */

Recordar. Una función tiene:
  • Parámetros: Identificadores útiles para alimentar con datos las operaciones de la función. Ejemplo: function operar(nombre, edad){...}
  • Argumentos: Datos insertados en la función cuando es invocada. Ejemplo: operar("Dudú", 45)

FUNCIONES RECURSIVAS

Para Svekis y Putten[5] significa llamar a una función dentro de sí misma tantas veces hasta que una condición dada sea true.

// Función con parámetro 'a'
function entrar(a){
/* Muestra parámetro –con 
su argumento insertado– */
console.log(a);

/* Si el valor del parámetro 'a' 
es menor a cero, detener ejecución. */

/* Si el valor del parámetro 'a' 
es mayor a cero, repetir ejecución. */
if (a > 0) {
/* Este incremento da el valor después del incremento */
entrar(--a);
}
}     

// FUERA DE LA FUNCIÓN:
/* Aquí se ingresa parámetro */
entrar(3);
//-> 3 2 1 0

En el ejemplo dado arriba se creó un bucle If dentro de la función entrar(a) -donde 'a' es el identificador del parámetro. Al final de ese bucle se reasignó el valor del parámetro mediante el signo 'doble negativo'. Es allí donde ocurre la recursión o llamado a la propia función. Ese tipo de signo decrementa en una unidad el valor del parámetro. La lógica de la ejecución del código anterior sería la siguiente:

 /*** LÓGICA DE EJECUCIÓN ***
* Mostrar argumento
      :: Argumento 3 es menor a cero? false.
      :: Argumento - 1 = 2
* Mostrar argumento
      :: Argumento 2 es menor a cero? false.
      :: Argumento - 1 = 1
* Mostrar argumento
      :: Argumento 1 es menor a cero? false.
      :: Argumento - 1 = 0
* Mostrar argumento
      :: Argumento 0 es menor a cero? True.
      :: Detener ejecución
***/

En otro ejemplo de recursividad:

En Javascript

// Función y argumento
function factorial(n) {
/* Condición del argumento: 
Si 'n' resulta en cero ó 
es menor a cero */
if (n === 0 || n < 0) {
// Devolver
return 1;
}
// Si 'n' NO resulta en cero
/* Se multiplica 'n' POR el resultado 
de restar 1 al resultado de factorial(n) */
return n * factorial(n - 1);
}

console.log(factorial(8));
//-> 40320

En otro ejemplo de recursividad (abajo), una función inicial interactúa con su bucle condicional interno IF/ELSE hasta que la condición se cumpla:

 // Función y parámetro 'a'
function funcRecursivo(a){
console.log("Muestra desde funcRecursivo():", a);

      // Si (a > 0) es TRUE... 
      if (a > 0){
            // Actualiza función y parámetro
            funcRecursivo(a - 1);
            
            // Si (a > 0) es FALSE...
            // Solo una vez pasa por aquí la iteración
            } else {
            console.log("** Muestra desde condicional **");
            }

console.log("Muestra desde el final de funcRecursivo() ", a);
}

// Invocar e ingresar argumento
funcRecursivo(3);

El resultado del código ya mencionado es el siguiente:

 /* Muestra desde funcRecursivo(): 3
Muestra desde funcRecursivo(): 2
Muestra desde funcRecursivo(): 1
Muestra desde funcRecursivo(): 0
** Muestra desde condicional **
Muestra desde el final de funcRecursivo()  0
Muestra desde el final de funcRecursivo()  1
Muestra desde el final de funcRecursivo()  2
Muestra desde el final de funcRecursivo()  3
*/

UTILIDAD DE LAS FUNCIONES RECURSIVAS

DESEMPEÑO DE FUNCIONES RECURSIVAS

Para Svekis, Putten[5] y el una función recursiva consume más recursos que un bucle común.


Una llamada interna mal implementada puede crear un bucle infinito o desbordamiento de pila –stack overflow– cuando al cumplirse la condicional esta no detiene el bucle. Para funciones recursivas es muy importante definir bien la condicional base para que detenga el bucle cuando la condición se cumpla.



Ejemplo de función recursiva infinita en Javascript

// Se declara función
function infiniteRecursion() {
/* Al activarse la función 
se vuelva a llamar a la función. NO HAY condición base
que filtre la ejecución. */
  infiniteRecursion();
}

// Se invoca función
infiniteRecursion();
//-> RangeError: Maximum call stack size exceeded

FUNCIONES CON PARÁMETROS PREDEFINIDOS

JS permite agregar valores previamente ingresados -default parameters- que serán invocados cuando la función este ausente de argumentos.

function desayuno(a = "encebollado", b = "albacora"){
console.log(a + " de " + b);
}
desayuno();
// -> encebollado de albacora

Sin embargo, si se ingresan nuevos argumentos, estos reemplazarán a los predefinidos:

function desayuno(a = "encebollado", b = "albacora"){
console.log("* " + a + " de " + b);
}
desayuno("fritada", "verde");
// fritada de verde
desayuno("cebiche", "camarón");
// cebiche de camarón

// Sin argumentos
desayuno();
// Muestra los predefinidos

/* 
* fritada de verde
* cebiche de camarón
* encebollado de albacora
*/

FUNCIONES INTERNAS

Son anidaciones de funciones:

function global(param) {
// Función global usa el valor de 'param' para operar
return param * 1000;

// Función interna con nuevo parámetro
    function internaYlocal(datito) {
    // Devuelve 'datito' operado
    return datito + 100;
    } // Cierra internaYlocal

/* Devuelve internaYlocal con parámetro 'param' */
return 'The result is ' + internaYlocal(param);
/* internaYlocal recibe el resultado 
de global(param); y luego transforma 
ese valor con la operación de internaYlocal(datito)
*/
}

console.log(global(3));
// -> 3000

/* La función internaYlocal 
recuerda el contexto de ejecución 
de la del outer scope */

FUNCIONES QUE RETORNAN FUNCIONES -o el uso del doble paréntesis–

Para Antani y Stefanov[4], "Una función solo retornará un solo valor –sea mediante return, sea undefined–; o retornará otra función". Así mismo, la función global puede estar inserta en un identificador de variable y luego, solo ese identificador, junto con '()', podrá ser invocado.

Pero entonces, ¿cómo diablos se invoca la función global y la función interna desde un solo llamado?. Con doble paréntesis: identificadorDeFuncion()();

// Función global
function a() {
console.log('Retorna desde global');

  // Retorna función interna anónima
  return function() {
  console.log('Retorna desde interno');
  }
}

/* Invocar ambas funciones pero 
INVOCANDO la función 'a' y con doble paréntesis */
a()();

//-> 
/* 
Retorna desde global
Retorna desde interno
*/

CONTENIDO DE UNA FUNCIÓN

JS permite agregar más parámetros a una función. El parámetro rest muestra todos los argumentos ingresados en cada parámetro de una función.

Parámetro rest

El parámetro rest permite agrupar en un arreglo todos los argumentos insertados en una invocación. El parámetro se lo crea agregando '...' junto a un nombre identificador de parámetro. Para Antani y Stefanov[4], rest crea un real objeto arreglo con los argumentos (p. 78).

Para el parámetro rest colecciona y da una cantidad indefinida de argumentos en forma de un arreglo.

En Javascript

/* Función 'sum' con parámetro rest. Este agrupará 
los argumentos dentro de un arreglo de nombre 'obtenerArgs' */
function sum(...obtenerArgs) {
let result = 0;

/* Bucle itera arreglo obtenerArgs y 
cuenta los argumentos */
for (const arg of obtenerArgs) {
/* 'result' guardará la suma de cada valor 
de cada argumento iterado */
result += arg;
// result = result + arg;
}
return result;
}

console.log(sum(10, 20, 5)); 
//-> 35

Antes de seguir, recordemos lo que es un parámetro y un argumento:

El parámetro rest -descanso en inglés- solo puede ser el último y el único dentro de los paréntesis de la función; y solo será usado allí.

En el ejemplo de abajo, la función 'saludos' solo tiene dos parámetros –el último tiene el operados rest '...' al inicio del identificador–. Gracias a eso, los dos últimos argumentos se convertirán en un arreglo de elementos al invocar la función 'saludos'. Recordemos nuevamente: el parámetro es el nombre identificador dentro de los paréntesis de la función. En cambio, el argumento es el dato ingresado al invocar la función y recibido por el parámetro respectivo.

 // Función de solo dos parámetros
function saludos(param1, ...param2){
// Muestra los dos parámetros
console.log(param1, param2);
}

/* Pero ingresamos más de dos argumentos
al invocar a la función */
saludos("Hello", "Holi", "Holaaaa!");
//   param1  | param2
//-> Hello [ 'Holi', 'Holaaaa!' ]

 /* Esta función solo tiene dos 
parámetros. En cada parámetro 
va un argumento */

/* Pero el identificador 'muchomas' 
aceptará más de un argumento  */
function palabras(algo1, algo2, ...muchomas) {
  console.log(algo1);
  console.log(algo2);
  console.log(muchomas);
}

/* Se agregan argumentos. 
La función por sí misma los mostrará*/
palabras("dudu", 
"romero", "Guayaquil", 
"esto otro", "esto otro", 
"esto otro");

/* 
dudu, romero, [ 'Guayaquil', 
'esto otro', 'esto otro', 
'esto otro' ]
*/

PARÁMETROS YA DETERMINADOS (DEFAULT PARAMS)

Si los argumentos –en su forma de valores– son insertados desde fuera de la función, también se pueden crear parámetros con argumentos ya asignados. Incluso, se pueden usar los mismos parámetros para crear operaciones y nuevos datos:

// Para plantilla literal
var moneda = "$";

/* Función con argumentos determinados. 
El segundo argumento opera con el primero */
function ahorro(dudu=20, brutanza=dudu*10/2){    

// Las plantillas lit. usan los valores de los argumentos 
console.log(`Dudu tiene ${moneda} ${dudu}, 
brutanza ${moneda}${brutanza}`)
}

/* Aunque solo se de un solo argumento, 
se mostrará el otro -ya asignado– 
con un dato */
ahorro(10);
/* ->
Dudu tiene $10, 
brutanza $50
*/

ÁMBITO O ALCANCE (SCOPE) DE DATOS DESDE FUNCIONES

Alcance –o scope en inglés– refiere a los límites en los cuales los datos pueden ser alcanzados.

Usar un ámbito local es igual al pescador que solo usa el lago más cercano para pescar. Pero si no logra pescar allí, buscará otro ámbito más externo –otro lago–.

Así mismo, JS ubica los valores de una variable desde lo más cercano hasta lo más alejado, pero solo desde un ámbito local. Para [min. 1.26] "ámbito –o alcance– es la parcela desde donde pueden ser accedidas las variables" y sus valores. Para Svekis, Putten y Percival[5], "toda variable dentro de un ámbito o contexto puede accederse; pero no desde fuera de un ámbito".

Ámbito global y ámbito local en vinculación de indentificadores de variables en funciones de Javascript. duduromeroa.com
Las flechas amarillas indican que está permitido buscar variables en ámbito local o global; o desde local hacia global. La flecha blanca indica lo imposible: no se puede pescar variables desde afuera hacia dentro de la función.

La función 'olfateará' la variable desde su propio cuerpo de función –ámbito local–. Si la encuentra, 'olfateará' fuera de esa función -ambito global-. Es decir, buscará desde dentro hacia fuera.

En el ejemplo de abajo, la función halla una variable de nombre 'fuera' en el ámbito –o alcance– global de la función.

var fuera = "Estoy afuera";

function buscar(){
return fuera;
/* Dentro del cuerpo de 
la función no hay ninguna 
variable con el identificador 
'fuera'*/
}

/* Al no hallar nada 
dentro de la función, 
esta ubicó al identificador 
'fuera', en el ámbito global*/

console.log(buscar());
// --> Estoy afuera

Sin embargo, cuando la variable está dentro de la función –ámbito local–, se accede directamente a esa pero obviando la variable del ámbito global –aunque sea la misma variable–.

var fuera = "Estoy afuera";
function buscar(){

/* La función 'pescó' la variable 
'fuera' en su propio ámbito local. 
Ya no es necesario buscar más.*/

var fuera = "Estoy dentro";
return fuera;
}
console.log(buscar());
// --> Estoy dentro

En otro ejemplo, un simple parámetro no puede ser llamado desde una invocación externa. Todo parámetro de función se lo considera como una variable local. Y al ser local, no puede ser accedido 'desde fuera':


function llamar(local){
console.log(local);
}

/* Se alimenta el parámetro con el argumento 'dato'. 
Y cuando es invocado puede ser accedido. */
llamar("dato");
// -> dato

/* PERO -> La línea más abajo dará error.
No podemos invocar un valor local 
desde un ámbito global. */

// console.log(local);

En otras palabras, es imposible 'pescar' variables desde un ámbito global hacia el ámbito local de la función. Esto es así ya sea para proteger los datos internos o para no entorpecer el acceso a estos, entre otros.

function buscar(){
var dentro = "Estoy dentro";
return dentro;
}

// Invocamos función llamando a variable local
console.log(buscar());
//-> Estoy dentro

/* ERROR al intentar llamar DESDE FUERA 
a la variable dentro de función */

// console.log(dentro);

EN RESUMEN

Únicamente se puede acceder y mostrar los valores internos de la función desde dentro de la función –es decir, desde un ámbito local–. Desde fuera de la función –ámbito global– es IMPOSIBLE hacerlo.



var a = "Afuera";

function f(a) {
// Se pide mostrar 'a'
console.log(a);
return a;
/* Este llamado retorna el valor 
de 'a' pero desde DENTRO de la función` */
}

// Se alimenta parámetro 'a' de la función 'f'
f("Dentro");

/* Este llamado retorna el valor 
de 'a' pero desde FUERA de la función` */
console.log(a);

En otro ejemplo identificaremos el error al llamar a un valor anidado y la solución según :

/* ¿PORQ NO FUNCIONA? */
function fuera(){
var x= "aqui";
return x;

    // Anidada en función fuera()
    function dentro(x){
    x= "aqui profundo";
    return x;
        
        // Anidada en función dentro()
        /* Imposible acceder desde fuera de la función fuera() */
        function muyDentro(x){
        x= "aqui muy profundo";
        return x;
        }
    }
} 
console.log(muyDentro());
// --> ReferenceError: muyDentro is not defined

Para poder acceder a la variable anidada se debió retornar las funciones más internas para luego, invocarlas en orden de anidación:

En Javascript

// Se declara función sin parámetro
function fuera() {
var x = "aqui";

      /* En esta función anidada sí se parametriza 'x' */
      function dentro(x) {
      x = "aqui profundo";

        /* En esta otra función anidada se vuelve a parametrizar 'x'
        pero también se retorna 'x'  */
        function muyDentro(x) {
        x = "aqui muy profundo";
        return x;
        }
    /* Se retorna muyDentro() para que esté 
    disponible en caso de ser invocada */
    return muyDentro;
    }
    /* Se retorna dentro() para que esté 
    disponible en caso de ser invocada */
    return dentro;
    }

// Variable guarda invocación a fuera()
var dentro = fuera();

// Variable guarda invocación a dentro()
var muyDentro = dentro();

// Se invoca valor más anidado en muyDentro()
console.log(muyDentro());
// --> "aqui muy profundo"

/* Se invocó funciones desde dentro hacia afuera 
SIEMPRE que las funciones hayan sido retornadas */

// fuera() --> dentro() --> muyDentro()

ÁMBITO GLOBAL Y VARIABLE VAR

La palabra clave var da a un valor el estado de dato global. Un estado de global puede ser accedido desde cualquier lugar del código. Y así mismo, un valor declarado con var puede ser actualizado o re-declarado posteriormente.

En el ejemplo de abajo, el identificador de nombre 'global' guarda un valor de texto que puede ser 'leído' desde la función 'llamar()'. Como veremos en los siguientes párrafos, un valor declarado dentro de la función 'llamar()' tendrá un estado diferente: este es, un estado o ámbito local. Lo que significa que ese valor ha sido creado y será accedido únicamente desde dentro de la función. Finalmente, el error ReferenceError: interno is not defined mostrado en el ejemplo inferior es causado por el intento de acceder a ese valor local desde fuera de la función.

var global = "* Vengo desde afuera...\n";

function llamar(){
// variable local
let interno = "* Vengo desde dentro";
// Invoca variable global o externa
console.log("* Llamo desde dentro,\n", global);
// Invoca variable local o interna
console.log("* Llamo desde dentro,\n", interno);
}

// Invocar función
llamar();

console.log("\n" + "* Llamo desde fuera --");
// Invoco variable externa
console.log("* Llamo desde fuera,", global);

/* -->
Llamo desde dentro, vengo desde afuera...
Llamo desde dentro, vengo desde dentro

-- Llamando desde fuera --
Llamo desde fuera, vengo desde afuera...
*/

/* ERROR */
// console.log(interno);
// ReferenceError: interno is not defined

ÁMBITO LOCAL Y VARIABLE LET

Para la clave let da a un valor un ámbito –o alcance– local. En otras palabras, un valor con let solo es accesible desde dentro del propio contexto –bloque de código o función– en el que fue creado.

En el ejemplo de abajo: aunque las variables tengan el mismo identificador, hay diferencias entre cuál valor es global o local. Gracias a let, solo un valor puede ser accedido desde su propio contexto de creación.

LET NO SE RE-DECLARA

Dará error volver a declarar una variable let dentro del mismo bloque o reinicializarla con var. Así mismo, let sí debe ser declarada antes de inicializada.


// Ámbito Global
let  x = "* 'x' Invocado desde fuera -global-";

{// Bloque 1
{// Bloque 2
{// Bloque 3

// Ámbito Local con nuevo valor de 'x'
let x = "* 'X' invocado desde dentro -local–";
// Se invoca 'x' local
console.log(x);

// ERROR! let nunca se redeclara
// let x = "Holi";
}
}
}
// Se invoca 'x' global
console.log(x);
// --> Solo desde dentro
// --> Desde Fuera

ÁMBITO LOCAL Y VARIABLE CONST

Para la palabra clave const también otorga ámbito únicamente local. Solo es accesible desde dentro de su contexto de creación y tampoco puede ser reinicializado con otro valor. Finalmente, debe ser usado solo cuando se requiera un valor que no cambiará a lo largo de la ejecución del código.

{ //Bloque1
{ //Bloque2
// Declara constantes
const num = 2022;
const nombre = "Edu";
// Se invoca ambos valores constantes
console.log(nombre + "-" + num);

// ERROR. Nunca se redeclarar
// const num = "holi";
}
}
//-> Edu-2022

/* ERROR. NO se puede acceder desde fuera 
del ámbito local del bloque más interno */
// console.log(num);

CIERRE -O CLOSURE- DE FUNCIONES

El anglicismo closure -algo que cierra– refiere en JS a una función que ejecuta actividad ubicada dentro de otra función, sin que sus datos internos 'contaminen' ejecuciones de nuevos contextos. Cada función creada es un nuevo contexto con su propio límite de acceso a valores. Recordemos que el contexto de ejecución es todo conjunto de datos y ejecuciones propias de un algoritmo o acción de código creado desde un único contexto.

En Javascript

function afuera(x) {

/* OJO: Un return fuera y 
otro dentro. Ambos return NO 
impiden la ejecución de la función */
  return function adentro(y) {
// Return de la función
    return x + y;
  };
}

/* Variable contexto: guarda la invocación 
de la función afuera() con un argumento valor de '5' */
// x = 5
let contextoA = afuera(5);

// Muestra operación del primer contexto
// 5 + 3
console.log(contextoA(3)); 
// -> 8

// Muestra operación del primer contexto
// 5 + 9
console.log(contextoA(9));
// -> 14 

/* Las invocaciones aún conocen que x = 5, 
a pesar de que las funciones han sido invocadas, 
ese valor en memoria no se ha perdido. */

El cierre de función es posible porque sus datos internos, una vez captados por la invocación de función, se cierran para dar paso a nuevos contextos –y por ende, a nuevas operaciones de datos– sin que el valor del argumento se haya perdido. Iniciaré el ejemplo con el código abajo. Pero salta a la vista una característica: ¿Por qué se debe guardar la función padre() dentro de la variable 'ejecutar'?. Se responderá eso en los siguientes párrafos.

// CREAR UN CIERRE
function padre(a){
// Dato global
var num = 10;

/* padre() retorna una nueva función 
interna con nuevo parámetro */
return function hijo(b){

/* Que a su vez, retorna una ejecución con:
- El parámetro de padre() 
- El paremetro de hijo()
- El valor de num */
return a + b + num;
}
}

/* padre(1) en variable 
con nuevo ARGUMENTO */
// Ahora 'a'= 5 
// Ahora 'num'= 10 
let ejecutar = padre(5);

// Ahora 
console.log(ejecutar(5));
// Se invoca SOLO la variable CON OTRO ARGUMENTO
// a + b + num;
// 5 + 5 + 10 

// --> 20

En otro ejemplo:

// Función padre con parámetro
/* Aunque 'x' este dentro de funcInterna() 
tendrá acceso 'desde fuera' a ese valor */
function funcExterna(x) {
// Retorna nueva función con nuevo parámetro
  return function funcInterna(y) {
// Devuelve ambos parámetros
    return x + y;
  }; // CIERRE funcInterna(y)
}

// Ahora x = 5
var insertar = funcExterna(5);

// Ahora y = 3
console.log(insertar(3)); 
// 5 + 3;
//-> 8

En otro ejemplo con eventos de DOM: en este caso, el valor de 'contador' se preservará entre invocaciones de función, pudiéndose actualizar el valor de 'contador' en cada invocación.

En HTML

<button id="boton">Toca acá</button>
<p id="text"> </p> 

En Javascript

// Variable guarda el apuntar al elemento HTML con id='boton'
var botoncito = document.getElementById("boton");

// A 'botoncito' se agrega evento mediante método click
botoncito.addEventListener("click", function() {
/* Este manejador de evento tendrá acceso a 
la variable contador desde acá afuera hacia 
la función retornada más abajo, 
AÚN DESPUÉS de haber retornado la función */

// Se abre variable
var contador = 0;

// 
return function() {
/* Este contador tendrá su valor preservado 
entre las invocaciones o llamadas al evento. 
Por lo que en cada click, el valor será actualizado. */
contador++;
// Apunta a elemento id'text' y se inserta string + variable
document.getElementById("text").innerHTML = 
"Haz hecho click " + contador + " veces.";
};

// Función que se autoinvoca
}());

Recordemos que las funciones nos tienen prohibido acceder –peor alterar– desde fuera de la función a un valor dentro de ellas. Eso es útil para proteger los datos de la función. En el ejemplo de abajo, es imposible acceder desde fuera de la función al resultado de la operación alojada en la variable 'b'.

function cerco(a){
var b = a * 1000;
return b;
}
// Función y argumento SÍ responden
console.log(cerco(2));
// -> 2000

// Llamado a valor interno NO responde

// console.log(b);
// Da error

Ocurre algo diferente cuando los datos simplemente están agrupados en bloques anónimos; es decir, entre { }. Allí todo es accesible, aunque sin identificador –no es una función ni tampoco tiene nombre–. Sin embargo, esa estructura es recomendada solo cuando se requiere ejecutar algo una sola vez. En otras palabras, el ejemplo de abajo no nos será útil para explicar los closures o cierre de función.

{ var cuevaUno = "oscuro"; {
    var cuevaDos = "más oscuro"; {
      var cuevaTres = "bieeeen oscuro"; 
      }
    }
  }
/* Aunque esté anidado, el valor de cuevaTres
se lo puede invocar desde el 
nombre identificador */
console.log(cuevaTres);
// bieeeen oscuro

Por lo tanto, la propiedad cierre o closure nos permitirá producir nuevos valores entre funciones anidadas. El cierre sucede porque una vez obtenido el valor dentro de la función, ese mismo valor está alistado y fijo para que otra invocación pueda accederlo de ser necesario.

Los closures permiten ejecutar datos desde uno o más contextos. Eso es posible porque la función se cierra –y se abre, en concepto– para nuevas operaciones. Para el closure o cierre de función permite "mantener un estado [diferente] en una variable [que a su vez] guarda una función".

En el ejemplo de abajo –basado en la explicación dada por , se guarda la función padre() en una variable para así permitir generar nuevos argumentos en cada nuevo contexto de ejecución –variables: ejecutar, ejecutar1, ejecutar2–.

Luego, se invoca cada variable creada –es decir, cada nuevo contexto– para dar utilidad a la función padre(). Cada nueva invocación contiene nuevo un argumento y por lo tanto, un nuevo resultado. Los valores entre contextos no se mezclan. Por lo que los cierres de función permiten generar nuevos datos limpios en cada nuevo contexto de ejecución.

/* Una función anidada 
común y corriente */
function padre(a){
var num = 10;

// padre() retorna una función interna
return function hijo(b){

// que a su vez, retorna una ejecución
return a + b + num;
}
}

/* ------* EL CLOSURE ES EVIDENTE 
DE AQUÍ EN ADELANTE *------ */

/* Gracias al cierre de función, se pueden 
generar nuevos contextos de ejecución 
desde nuevos argumentos */
let ejecutar = padre(10); 
/* De aquí en adelante, el argumento 
de padre(10) se encierra y no influenciará 
en ningún otro lado excepto en esta ejecución */

/* NUEVA variable con 
NUEVO argumento -nuevo contexto- */
let ejecutar1 = padre(20);
/* También se cierra */

/* NUEVA variable con 
NUEVO argumento -nuevo contexto- */
let ejecutar2 = padre(30);
/* También se cierra */

/* Nuevo ARGUMENTO -el anterior no influye– */
console.log(ejecutar(100));
// --> 120

/* Nuevo ARGUMENTO -el anterior no influye– */
console.log(ejecutar1(200));
// --> 230

/* Nuevo ARGUMENTO -el anterior no influye– */
console.log(ejecutar2(300));
// --> 340

Finalmente, un ejemplo más de cierre de función. Al momento de generar nuevo argumento -nuevo nombre- el argumento anterior se cierra para dar paso al nuevo dato.

/* Una función con un parámetro */
function padre(nombre){

/* Variable dentro de ámbito de función */
let apellido = "Romero";

/* Función anidada sin parámetro */
function hijo(){

/* Evento */
console.log("Mi apellido es " + nombre + apellido);
}

/* Se llama a la función hijo para que 
ejecute LUEGO de que función padre ejecute  */
hijo();    
}

/* Ahora, cada llamado de función padre() 
con nuevo argumento llamará a función 
anidada y generará su propio resultado. */
// Nombre diferente
var saludo = padre("Dudu ");
// Nombre diferente
var saludo = padre("Juan ");
// Nombre diferente
var saludo = padre("julia ");

/* 
Mi apellido es Dudu Romero
Mi apellido es Juan Romero
Mi apellido es julia Romero
*/

Un ejemplo de yapita, pero recordar: cada nueva función crea nuevo ámbito nuevo, donde los valores de variables no deberían mezclase. Gracias al closure se puede operar nuevos valores insertados como argumentos.

 /* Función con un parámetro */
function pedidos(tipoPedido){

/* Constante guarda plantilla 
literal ${ argumento } con argumento insertado */
const pedido = `Pedido: ${tipoPedido}`

/* Función interna es 
retornada con un parámetro */
return function(item){

/* Retorna const 'pedido 
con nuevo parametro 'item' */
return `${pedido} ${item}`
}
}

// Se crea variable para cada contexto
// JUGOS
const facturadoJugo = 
pedidos("jugo")
// POSTRE
const facturadoPostre = 
pedidos("postre")

/* Se invocan contextos 
con nuevo argumentos */

// JUGOS
console.log(
facturadoJugo("naranja"));

console.log(
facturadoJugo("coco"));

// POSTRES
console.log(
facturadoPostre("maracuya"));

console.log(
facturadoPostre("batido fruta"));

//-> Pedido: jugo naranja

CADENA DE ALCANCE -scope chain–

Cadena de alcance refiere a la secuencia de búsqueda desde lo más interno hacia lo más externo al momento de referenciar un valor que ha sido invocado en una función.

Para "cuando una función es definida dentro de otra función, esta tiene acceso a las variables de la función padre; pero la función padre no tiene acceso a las variables de la función hijo".

En Javascript

function afuera() {
// Declara variable
var x = 10;

// ERRROR!
/* La función padre NO tiene acceso a los 
valores de la función hijo */
// console.log(p);

// Función interna
function dentro() {
/* Se invoca o referencia el valor de 'x'. 
La función hijo SÍ TIENE acceso a los valores 
de la función padre. */
console.log(x);
var p = 20;
}
// Se invoca la función más interna dentro();
/* Inicia la búsqueda del valor de 'x' PRIMERO 
desde lo más interno -función dentro(). 
Al no hallarse el valor de 'x' allí, el sistema 
busca en la FUNCIÓN PADRE más externa */
dentro();
}

// Se invoca la función externa
afuera();
//--> 10

En los dos ejemplos siguientes se tiene que en el primero sólo se puede acceder a la variable de la función padre. Y en el segundo ejemplo solo se puede acceder a la variable de la función hijo. Similar a poder entrar solo al jardin de una casa –ejemplo A– o solo a la sala –ejemplo B–.

En Javascript

// EJEMPLO A
// Para acceder SOLO al valor de función EXTERNA
function jardin1(){
let enJardin1 = "* Solo entra al JARDIN.";

// ERROR: No se accede a valores de la función hijo
// console.log(enSala1);

function sala1(){
let enSala1 = "* Solo entrar a la SALA";
// Devolver variable en función PADRE
return enJardin1;
}
return sala1();
}

/* Invoca función más interna para entrar al Jardin */
console.log(jardin1());
// * Solo entra al JARDIN.

// - - - 
// ERROR: No se accede a valor interno
// console.log(enSala);

// ERROR: No se accede a función interna
// console.log(sala());

// -+-+-+-+-+  -+-+-+-+-+  -+-+-+-+-+

// EJEMPLO B
// Para acceder SOLO al valor de función INTERNA
function jardin2(){
let enJardin2 = "* Solo entra al JARDIN.";

function sala2(){
let enSala2 = "* Solo entrar a la SALA";
// Devolver variable en función HIJO
return enSala2;
}
return sala2();
}

/* Invoca función externa para entrar a la sala */
console.log(jardin2());
// * Solo entra al JARDIN.

Cadena de alcance refiere a la secuencia de búsqueda desde lo más interno hacia lo más externo al momento de referenciar un valor que ha sido invocado en una función.

// Valor global: accesible por todos
var global = 1;

/* Desde dentro, cada llamada se hace 
a los valores dentro de la función */
function externo() {
// Variable local
var externo_local = 2;

function interno() {
// Variable local
var interno_local = 3;
/* Devuelve variable local y de función padre */
return interno_local + externo_local + global;
}
// Se devuelve función interno() que obtendrá externo()
return interno();
}

// Esto se ejecuta así: desde externo hacia interno
console.log( externo() );
// -> 6

Otro ejemplo de closures:

var a = "global variable";

var F = function () {
var b = "local variable";

var N = function () {
var c = "inner local";

// Tiene acceso a función primera 'b'  o segunda 'c'          
return b; };
/* Devuelve 'N' para que funciones 
el llamado desde dos paréntesis */
return N; };

/* // Doble paréntesis porque
hay al menos una función anidada */
console.log( F()() );
// --> local variable
// Devuelve la variable 'b'

Se aclara que toda función interna tiene acceso a variables externas pero dentro del ámbito de su función padre:

function padre() {
// Variable local desde padre()
var apellido = 'Romero'; 

/* hijo() es interno, es accedida desde padre(), 
y llama a valor de padre() */
function hijo() {
console.log(apellido); 
} 
// Desde dentro de padre() se invoca hijo()
hijo();
}

// Desde fuera, se invoca padre()
padre();
// Romero

En resumen, un cierre de función –function closure- comprende los vínculos internos ejecutados solo desde un ámbito local. Para Antani y Stefanov[4] "(...) se crea un cierre cuando una función mantiene un enlace a su ámbito principal incluso después de que el principal haya regresado. Y cada función es un cierre porque, al menos, cada función mantiene el acceso al ámbito global, que nunca se destruye."(p. 106)

Para "Un cierre es la combinación de una función y del entorno léxico –el acceso desde funciones hijo a variables contenidas en funciones padre– dentro del cual se declaró esa función. Este entorno consta de cualquier variable local que estuviera dentro del alcance en el momento en que se creó el cierre."

Sin embargo, anidar funciones dentro de otras funciones afecta el rendimiento del código y el consumo de memoria.

FUNCIONES FLECHA

Alternativa breve para expresión de funciones agregada en la actualización ECMAScript del 2015. Según en mdn web docs las funciones flecha tienen las siguientes limitantes en su uso:

LIMITACIONES EN EL USO DE FUNCIONES FLECHA
  • No tiene sus propios enlaces a this o super y no se debe usar como métodos.
  • No tiene argumentos o palabras clave new.target
  • No apta para los métodos call, apply, bind que generalmente se basan en establecer un ámbito o alcance
  • No se puede utilizar como constructor.
  • No se puede utilizar yield dentro de su cuerpo.

Un muy sencillo ejemplo lo da Eric Elliot en su libro Composing Software (2017). Donde una función flecha, alojada en una variable, omite gran parte de la sintaxis de una función común:

En Javascript

const num = n => n + 1; 
console.log(num(10));
// 11

En otro ejemplo dado por Elliot, el uso de variables globales y locales permite también usar funciones flecha para obtener datos desde las propias funciones:

class="parrJScode identificaLenguajeSnip">En Javascript

// Declaro
var numero;

// asigno valor
var numero = 100;

// o, reasigno expresión con valor
var numero = 100 * 100 / 2;

En Javascript

// Globales
const g = n => n + 1; 
let f = n => n * 2;

const ejecutar = valor => { 
     // Locales
     // Guarda el resultado desde la función 'G'
     const usarG_valor = g(valor); 
     // Guarda el resultado desde la función 'F'
     let usarF = f(usarG_valor); 
     // Retorna función 'f'
     return usarF;
};
// Añade valor a parámetro de función ejecutar()
console.log(ejecutar(20));
// 42

// console.log(usarG_valor);
// --> Error. No puede ser obtenido. Es local

// console.log(usarF);
// --> Error. No puede ser obtenido. Es local

console.log(g);
// Puede ser obtenido. Es global

Abajo, ejemplos de función tradicional y función flecha:

// Función tradicional
function tradicional(a,b){
return a+b;
} console.log("Tradicional: " + 
tradicional(2,2) );
// 4

/* En las funciones flecha 
se ELIMINAN 'function', {}, y 'return*/

/*  Función flecha con dos argumentos */
var func1 = (x, y) => x + y;
console.log("Flecha: " + func1(4,4) );
// Flecha: 8

/*  ...SIN argumentos */
var func2 = () => 2 + 2;
console.log( func2() );
// 4
// Recordar que no hay argumento

/*  ...CON MÁS de un parámetro */
var func3 = (a, b, c, d) => a + b + c;
console.log( func3(100,100,100) );
// 300

Usar funciones flecha alojadas en una variable permite reescribirlas y reutilizarlas invocando solo el nombre de la variable:

// Calcular edad según nacimiento
var edad = a => console.log( 2022 - a);
/* Ingresar como argumento año de nacimiento */
edad(1976);
// -> 46

Luego, –ejemplo abajo– con dos parámetros. Recordar agrupar la suma –y la resta– entre paréntesis para generar un solo resultado operable:

 /* Con dos parametros. 
OJO -> agrupamos suma para que 
su resultado sea luego operado */
var promedio = (a, b) => console.log( (a+b) /2 );
/* Ingresamos dos argumentos */
promedio(9.7, 9.2);
// 9.45

Así mismo, se puede combinar métodos como forEach() para recorrer y mostrar los elementos de un arreglo:

 const apellidos = [
"Julio", 
"Alfredo", 
"Jaramillo", 
"Laurido"];

// Recorrer arreglo con forEach() + función
apellidos.forEach(iterar => 
console.log(iterar));

/* 
Julio
Alfredo
Jaramillo
Laurido
*/

En un ejemplo más complejo el uso de métodos junto con funciones arrow ayuda a reducir el código a formas mejor leíbles:

En Javascript

// Arreglo
let numbers = [1, 2, 3, 4, 5];

/* Se crea variable. Se invoca  método reduce() 
para sumar todos los números de un arreglo. Dentro 
de reduce() una función arrow con dos argumentos. */
let sum = numbers.reduce( (total, num) => total + num, 0 );

/* Se crea otra variable que guarda invocación al método 
map() para aplicar eso en cada número y doblar el resultado. */
let square = numbers.map(num => num * num);
let doubleSquare = square.map(num => num * 2);

/* Se crea variable para invocar el método filter() 
para remover todos los números que no sean mayores a 20 */
let filtered = doubleSquare.filter(num => num > 20);

/* Nuevamente se crea variable para filtrar 
que sume y filtre 
-OJO COMO SE INVOCA MÉTODO SOBRE MÉTODO– */
let sumOfFiltered = filtered.reduce((total, num) => total + num, 0);

// Se invoca la última variable
console.log(sumOfFiltered); 
// 82

FUNCIONES FACTORY: factory function, factory pattern, o de fábrica, o de patrón

Según Eric Elliott en JavaScript Factory Functions with ES6+, (2017), una factory function devolverá (instanciará) un nuevo objeto aún cuando esa función no se comporte como una clase o como un constructor. Es decir, una factory function es una función hippie, relajada, que crea objetos "pero sin el ajetreo del new, o de las clases" (Elliott, 2017).

En otras palabras, una función factoría es una instanciadora de objetos, pero sin ser en sí misma una clase. La función factoría aprovecha la sintaxis de función flecha o arrow function, cuyo papel también es ser incansable para devolver ejecuciones.

En Javascript

class item {
     constructor(nombre) {
          this.nombre = nombre;
          console.log(`'${this.nombre}'`);        
  }
}

// Instancia desde CLASE mediante NEW
const instanciaDesdeClase = new item("DUDU");
// 'DUDU'

// Instancia desde FACTORY FUNCTION
const item1 = (nombre) => new item(nombre);
item1("DUDU instanciado desde función flecha"); 
// 'DUDU instanciado desde función flecha'

// Instancia desde FACTORY FUNCTION
const item2 = (nombre) => new item(nombre);
item1("ROMERO instanciado desde función flecha"); 
// 'ROMERO instanciado desde función flecha'

En otro ejemplo tomado de Elliott (2017), usamos una sintaxis que combina devolver el contenido de un objeto, un método, una referencia, y luego invocarlo reemplazando los valores:

En Javascript

const crearUsuario = ({ nombre, fotito }) => ({
  nombre, fotito,
  darNombre(nombre) {
    this.nombre = nombre;
    return this;
  }
});
console.log( crearUsuario({ nombre: 'dudu', fotito: 'fotoDudu.png' }) );
/* {
  nombre: 'dudu',
  fotito: 'fotoDudu.png',
  darNombre: [Function: darNombre]
}
*/

En otro ejemplo tomado de Elliott (2017), creamos una función patrón o pattern function y usamos su par de valores arbitrarios para generar nuevas instancias sin requerir constructor o new:

En Javascript

// Función patrón
const plantilla = () => { xxx: 'xxx' };

const ciudad1 = () => ({ ciudad: 'Guayaquil' });
const ciudad2 = () => ({ ciudad: 'Machala' });

console.log(ciudad1());
// { ciudad: 'Guayaquil' }
console.log(ciudad2());
// { ciudad: 'Machala' }

Luego, con ese mismo ejemplo, podremos instanciar solamente los nombres de las ciudades:

En Javascript

// Función patrón
const plantilla = (ciudad) => `ciudad: '${ciudad}'`;

const ciudad1 = () => plantilla('Guayaquil');
const ciudad2 = () => plantilla('Machala');

console.log(ciudad1());
// ciudad: 'Guayaquil'

console.log(ciudad2());
// ciudad: 'Machala'

DECLARACIONES

Por otro lado, para mdn web doc, "una declaración es una instrucción pura. Está separada de entre otras declaraciones con punto y coma". Mucho en JS es declarable: variables, constantes, funciones, clases. Para Flanagan [3] declarar es "crear y nombrar almaneces para valores –aunque luego los inicialicemos con algún valor–" y también los invocaremos para operarlos y producir. Vale aclarar que sentenciar y declarar tienen el mismo significado en un contexto de programación: ambas expresas que algo está preparado para ser operado, ejecutado, producido o evaluado.

No confundir con declarar una variable. Cuando declaramos una variable pedimos al sistema que alojaremos datos mediante uno de los tres tipos de alojamientos durante la ejecución del código en JS: let, var o const. Aunque una buena norma es declarar e inmediatamente asignar un valor a la variable (especialmente con const), daré el siguiente ejemplo de declaración de variables:

En Javascript

// DECLARO
var pais;
let continente;
/* OJO: SOLO con 'const' debemos
asignar valor de inmediato */ 
const ESTADO = "Republica";

// ASIGNO
pais = "Ecu";
continente = "América";

// Invoco variables
console.log(pais, continente, ESTADO);
// Ecu América Republica
NO CONFUNDIR
  • Una declaración o sentencia provee acción a otras sentencias o declaraciones. Sus acciones pueden ser detenidas o canceladas.
  • Una expresión es un conjunto de instrucciones que evalúa o procesa valores.
  • Una función es un conjunto de código que se escribe una vez, pero puede ser ejecutado varias veces.

// nombre de función y argumentos
function miGranja(costo, gallinas){

// Expresión para operar
// y mostrar valores
var calculo = costo * gallinas;

console.log(gallinas + 
" gallinas cuestan " + 
calculo + " dólares");
}

// LLamado de función y retorno de valores
// El primer valor es el costo unitario
// El segundo valor es la cantidad de unidades
miGranja(3.58, 15);
//--> 15 gallinas cuestan 53.7 dólares

En el ejemplo debajo, hay una función dentro de otra función. La función Number convierte un valor alfanumérico a un dato número. Luego, la función prompt permite un campo de ingreso de dato alfanumérico y al mismo tiempo, retorna el dato ingresado –ya convertido a número– en un resultado operado por la función document.write

/* Para que este código se active, 
debe estar insertado en un documento HTML. 
En los siguientes ejemplos solo se mostrará 
el código de JS */
// - - - - - 
<script>
// Se muestra el campo de inserción de dato
let num = Number(prompt("Elige un numero"));

/* Se muestra el número 
ingresado multiplicado por 10 */
document.write("Sumado con 10 da " + num * 10);
</script>
// - - - - - 



FUNCIONES DE REPETICIÓN O BUCLES

FUNCIÓN WHILE

Primero evalúa si condición es true; luego, ejecuta.


FUNCIÓN DO/WHILE

Primero ejecuta -aún si condición es false–; y luego, evalúa si condición es true -para volver a ejecutar-. Sino, cancele.



Ejemplo con bucle While:

/* Esto dará ERROR! porque 'num' 
no está inicializado con ningún valor */
while(10 < 11){
var num  = num + 1;
return console.log(num);
}
//-> NaN

/* Corregido */

// Se inicializa con cero
var num = 0;
// Loop
while(10 < 11){
// Se altera valor
num = num + 1;
/* Se retorna solo una vez, 
luego se detiene todo */ 
return console.log(num);
}

Otro ejemplo con bucle While:

// WHILE 
// Quiero cambiar un billete de...
var billete = 5;

// Queremos contar desde la moneda 1
var contador = 1;

/* Hasta que el cajero sea menor 
o igual a la cantidad del billete, 
entregue monedas */
while(contador <= billete){
console.log("Moneda número " + contador);

/* El contador debe incrementarse 
por unidad entregada*/
contador = contador + 1;
}
// --> Moneda número 1
// --> Moneda número 2...
// --> Hasta completar el billete

Por otro lado, el ciclo Do-While ejecutará alguna expresión solo una vez y luego, evaluará y ejecutará siempre que la condición sea TRUE.

¿QUÉ ES EL BUCLE INFINITO?

Flanagan [3] dice: "Ocurre cuando el valor de la expresión del while siempre seguirá siendo true, y cuando ese mismo valor nunca cambia. Si es siempre true, el bucle nunca se detendrá. Ya que a todo bucle while solo una condición de false lo cancela".


Para evitar un bucle infinito, "Uno o más valores de variables deben cambiar en cada iteración del loop", y así poder en algún momento cancelar el bucle.



// DO WHILE
// Guarda un nombre
 var nombre = "Bruno";

// Ejecuta una vez antes de evaluar...
 do {console.log("Si tu nombre es Bruno, entras!");
console.log("Soy " + nombre);

/* Se detiene si condición es false. 
El operador != invierte el valor 
de true a false */
} while (nombre != "Bruno") 

/* Ya que la condición es False, 
el bucle se detiene */
console.log("Detenido bucle");

FOR

El bucle FOR repite un bloque de código mientras la condición de un valor booleano sea verdad (true). Cuando ese valor booleano resulte falso el bloque de código ya no será ejecutado.

El bucle FOR es una unidad de un solo paréntesis. Contiene: (expresiónInicial; condiciónDeLaExpresión; alteraciónDeLaExpresión) { ..bloqueDeCódigo.. }

 for (valor; condiciónDelValor; alteraciónDelValor) { 
..bloqueDeCódigo.. 
}

En otras palabras, cada expresión es identificada por un punto y coma. Y mientras la condición –segunda expresión dentro del paréntesis– sea falsa, el valor inicial se actualizará y se mostrará:

 for (let a = 0; a <= 3; a = a + 1){
console.log(a);
}
// -> 0 1 2 3 

Al inicio puede ser complicado determinar dónde termina cada expresión de un bucle FOR. Así como ocurre en todo el código, cada expresión dentro de un FOR está separado por un punto y coma. Recordar eso.



En otro ejemplo -abajo–, un bucle FOR evita que un cajero de banco pague al usuario más dinero del que desea retirar. Si el pago alcanza el monto, entonces el cajero dejará de pagar.

// Valor del billete
let billete = 10;
// Cantidad a pagar
let aPagar = 30;
// Primer mensaje
console.log("** Pagar $" + aPagar  + " en billetes de $" + 
billete + " **");

// Bucle: Evaluará cada entrega hasta completar el pago
for (
// Declarar valor a evaluar
var recibido = 0; 
// Condición para evaluar
recibido <= aPagar; 
// Actualización del valor si condición es true
recibido =  recibido + billete){

console.log("Preparar: " + recibido + " billetes");
}
console.log("Se pagó al usuario $" + (recibido - billete) + 
". Cierra cajero" );

/* 
** Pagar $30 en billetes de $10 **
Preparar: 0 billetes
Preparar: 10 billetes
Preparar: 20 billetes
Preparar: 30 billetes
Se pagó al usuario $30. Cierra cajero
*/

En otro ejemplo (abajo), el paréntesis del bucle FOR contiene: declaración de variable, condición de la variable, y alteración que se ejecutará solo si la condición se mantiene como True:

// Al tercer bateo fallido, enviarlo a base
console.log("Inicia el juego. Lanzador 
envia la pelota al bateador...");

/* 
- Inicia variable; 
- Condición que evalua esa variable; 
- Actualización de valor –solo si condición es true-
*/
for (var bateo = 1; bateo <= 3; bateo = bateo +1){
console.log("* Bateo fallido " + bateo);
}

/* Si condición es false, se cancela bucle y 
sigue el código */
console.log("Fuera. Jugador va a base");

/* --> Inicia el juego. Lanzador 
envia la pelota al bateador...
* Bateo fallido 1
* Bateo fallido 2
* Bateo fallido 3
Fuera. Jugador va a base*/

Si se desea cancelar el bucle creado por el constructor for aún cuando la condición no resulte en false -que es la única forma en el que bucle se detenga y no evalúe más– se puede aplicar la declaración break para cancelar el bucle luego del primer resultado

console.log("Patea la pelota...");
for (var pateada = 1; 
pateada <= 10; 
pateada = pateada +1){
console.log("* Tuviste " + 
pateada + " oportunidad, 
pero no entró al arco ");

/* Aunque aún existen 9 oportunidades, 
el bucle se detiente a la primera */
break;
}
console.log("** FIN **");

FOR permite inicializar más de una variable ya declarada y operar más de una variable si condición resulta en true. Todo dentro del mismo paréntesis.

/* Un boxeador gana 2 puntos
si da un golpe */

/* Podrá recibir 
hasta 5 golpes */

// \n da un espacio para la línea de texto
console.log("** Inicia pelea..." + "\n");

// Se declaran variables
var golpesDados, 
golpesRecibidos, 
puntosGanados;

// Se inicializa más de una variable
for (
golpesDados = 0, 
puntosGanados = 0; 

// Evalua condición
golpesDados <= 5;

// Si es true:
// Suma un golpe dado y...
golpesDados = golpesDados + 1,

// Sume 2 puntos por cada golpe.
puntosGanados = golpesDados * 2 
){
// Cuerpo del FOR -bucle-
console.log(
      "Golpes dados: " + 
      golpesDados + "\n" +  
      "Puntos ganados: " + 
      puntosGanados + "\n" );
}
console.log("** Fin de pelea" + "\n");

/* ** Inicia pelea...
Golpes dados: 0 | Puntos ganados: 0
Golpes dados: 1 | Puntos ganados: 2
Golpes dados: 2 | Puntos ganados: 4
Golpes dados: 3 | Puntos ganados: 6
Golpes dados: 4 | Puntos ganados: 8
Golpes dados: 5 | Puntos ganados: 10
** Fin de pelea
*/

Se puede anidar FOR para repetir acciones, pero hay que tener cuidado en incrustar la variable en el ámbito adecuado para que sea accesible:

 // Da espacio de enter
var res = '\n';
var verticales = 0;

// bucle 
// Verticales
       for (verticales; verticales < 5; verticales++) {

/* var 'horizontales' debe estar dentro 
del ámbito de bloque. Sino, no es hallado por el FOR */
var horizontales = 0;
         for (horizontales; horizontales < 5; horizontales++) {
// Esta es la línea vertcontcal
           res = res + '* ';
      }

// Se actualcontza 'res'
/* Esta es la línea horcontzontal. 
Pues el '\n' es un espacconto de tecla */
res  = res  + '\n'; }

console.log(res)
// * * * * * 
// * * * * * 
// * * * * *  

Agrego un ejemplo interesante de bucle FOR, basado en Antani y Stefanov[4] (p. 69).

 // A
var res = '\n'
// B
var horizontal = 1;

// C)
for (horizontal; horizontal <= 7; horizontal++) {

var vertical = 1;
// D
for (vertical; vertical <= 15; vertical++) {
// E
res = res + ((horizontal * vertical) % 8 ? ' ' : '*');
}
// F
res = res + '\n'; }

console.log(res);
// -> Mira tú mismo el gráfico resultante 

La explicación del algoritmo es la siguiente:

Recordar condicional ternario. Solo evalúa booleano true o false:

// Dato a evaluar
var valor = 1; // Condicional
console.log(valor ? // En true 
"Cuando es true" : // En false
"Cuando es false"); // --> Cuando es true

Otro ejemplo con la combinación de FOR y el operador in. Este operador 'in' envia true si halla un valor -que no sea null o undefined– en el arreglo 'nombres'.

var nombres = ['Dudu', 'Romero', 'Andrade'];

/* 'ordenFinal' debe estar previamente 
declarada con un valor vacio */ 
var ordenFinal = '';

// Se declara variable cont
var cont = 0;

/*  Dice: hallar el valor de la variable 
'cont' en arreglo 'nombres' */
for (cont in nombres) {

/* Se actualiza 'ordenFinal':  */  
// nombres[cont] --> recorre cada elemento de 'nombres'
ordenFinal += cont + ' Nombre: ' + nombres[cont] + '\n';
}

console.log(ordenFinal);
/* 
0 Nombre: Dudu
1 Nombre: Romero
2 Nombre: Andrade 
*/

Finalmente, ejemplo de combinar una función, un FOR y una función anidada (abajo):

 /* Una función con dos parámetros.
Esta función será invocada más abajo. */
function numeros(x,y){
// Devuelve operación
return x + y;
}

// Crea arreglo vacío
let arregloNuevo = [];

/* GRAN ITERACIÓN: El propio FOR alimentará 
la función 'numeros'. Mientras iterador sea 
menor a 5, actualizar iterador -incrementa +1-  */
for (let iterador = 0; iterador < 5; iterador++){

/* En nueva variable, guardar función 
cuyo argumentos operen al iterador. No confundir: 
la función 'numeros' tiene dos parámetros: */

      /* Tener en cuenta: el primer argumento 
      de la función numeros tiene valor cero. 
      El segundo argumento multiplica lo que vale 
      iterador por dos */
      let result = numeros(iterador, 2*iterador);
      /* En arregloNuevo insertar el valor 
      de result -que es un argumento- */
      arregloNuevo.push(result);
}

console.log(arregloNuevo);
// -> [ 0, 3, 6, 9, 12 ]

SWITCH

Es una estructura de selección, comparación o correspondencia condicional. Necesita de la declaración break para dejar de ejecutar la comparación; y default para ejecutar cuando ninguna alternativa coincida con la condicional. La declaración default puede aparecer en cualquier parte de la estructura switch.

La declaración switch encierra en paréntesis la comparación base. En el cuerpo, cada declaración case contendrá el resultante de cada coincidencia. En cada case está la ejecución para esa coincidencia. "break; obliga al sistema a leer la siguiente coincidencia en caso que la anterior no se cumpla". En cambio, la cláusula return; devuelve un valor siempre que switch este dentro de una función.

/* Variable 'candidato' 
en switch declarado como string */
console.log("¿Con quién bailará Ana?" + "\n");
// OJO. No debe terminar en ';'
switch (candidato = "Tulio"){

/* Aquí se determinan las 
alternativas del candidato 
inicial de la variable 
contenida en 'switch' -arriba- */

case "Juan": 
console.log("Que baile con " + candidato)
// break: Si el caso se cumple, detener.
break;

// Si n es igual a 2
case "Pedro": 
console.log("Que baile con " + candidato)
break;

/* Si el valor alternativa no está 
en ningún case, entonces 
ir a default */
default:
console.log("Que no baile con nadie");
break;
}

/* --> ¿Con quién 
bailará Ana?

Que no baile con nadie 

(El nombre de Tulio 
no está en ninguna 
alternativa) */




SENTENCIAS BREAK y CONTINUE

Ambas son declaraciones de flujo de repetición en bucles.

BREAK detendrá prematuramente el bucle y saltará al bloque de código inferior inmediato –aún cuando la condición dada sea True-. Ejemplo:

En Javascript

// Bucle recorre desde cero hasta 9
for (let i = 0; i < 10; i++) {
// Condición
  if (i === 5) {
/* Se detiene bucle cuando condición sea TRUE. 
Es decir, solo los primeros cinco 
números se muestran */
    break;
  }
  console.log(i);
}
//-> 0 1 2 3 4

Con CONTINUE se detendrá el bucle en proceso y saltará a la siguiente iteración controlada por la condición. Ejemplo:

En Javascript

// Bucle cuenta de 0 a 9
for (let i = 0; i < 10; i++) {
// Condicional  
if (i % 2 === 0) {
/* Salta a la siguiente iteración SOLO 
cuando la condición es true (es decir, 
cuando i sea par). Por lo que solo los 
números impares son mostrados */

/* Cuando el valor es false, 
le sigue el llamado del valor de i*/
continue;
}
// Se muestra valor de i pero solo los que 
console.log(i);
}
// Output: 1 3 5 7 9

Se dará el siguiente código de ejemplo: en una discoteca se tiene permitido ingresar a una cantidad límite de personas –en este caso, 3–. El bucle para evaluar la condición y detenerla cuando se cumpla sería el siguiente:

// Valor limite de personas
let limite = 3;
// Bucle para evaluar cantidad limite
for (var ingresaron = 0; ingresaron < limite; ingresaron++){
// Muestra la cantidad de personas ingresadas
console.log("Ingresaron " + (ingresaron+1) + " personas" );
}
// Alerta: 
console.log("*** YA NO INGRESAN MÁS PERSONAS ***");
/* -->
Ingresaron 1 personas
Ingresaron 2 personas
Ingresaron 3 personas
*** YA NO INGRESAN MÁS PERSONAS ***
*/

En el código de ejemplo, agregar break; antes de cerrar las llaves {} generaría tan solo una iteración y luego, mostrar la alerta.

 for (..; ..; ..){
console.log(...);
break;
}
/* -->
Ingresaron 1 personas
*** YA NO INGRESAN MÁS PERSONAS ***
*/

Así mismo, usar la declaración continue permite ejecutar la iteración para volver a evaluar condición. Finalmente, para Svekis y Putten[4] el uso de estas declaraciones funciona mejor durante iteraciones de grandes cantidades de datos que provienen desde fuera del código. Por lo que usarlos en ejemplos sencillos no es práctico.

ETIQUETADO EN BLOQUE DE BUCLES (label-loop)

El etiquetado es un nombre de bloque de código que se puede referenciar más adelante en el código. Se lo realiza con una palabra identificatoria antes del inicio de cada bucle.

La etiqueta del bloque puede ser invocada luego de las sentencias break o de continue.

En Javascript

// Nombre del bucle
loop1:
// Declara valor de i
for (var i = 0; i < 10; i++) {
// Divide sección de cada bucle 
console.log("---");
// Declara valor de j
for (var j = 0; j < 10; j++) {
// Condicional
if (i * j > 30) {

/* Pausa el loop más externo, 
y no solo el interno */
break loop1;
}
// Aquí le llega cuando el break se activa
console.log(i * j);
}
}

/* --> 
---
0, 0, 0, 0, 0, 0...
---
0, 1, 2, 3...9
---
0, 2, 4...18
18
---
0, 3, 6, 9, 12...27
---
0, 4, 8, 12...28
*/

¿Y qué sucede cuando hay dos etiquetas, dos loops y la sentencia break?

En Javascript

// Loop externo. Itera con 'j'
outerLoop: for (let i = 0; i < 3; i++) {
// Loop anidado. itera con 'j'
innerLoop: for (let j = 0; j < 3; j++) {

/* Cuando los valores de 'i' y 'j' son 1, se pausa 
el loop outerLoop externo */
if (i === 1 && j === 1) {
// Solo pauso el loop externo
break outerLoop;
}
// Mediante plantilla literal muestro valores
console.log(`i: ${i}, j: ${j}`);
}
} // Cierar outerLoop

// ->
/* 
i: 0, j: 0
i: 0, j: 1
i: 0, j: 2
i: 1, j: 0
*/

¿Y qué sucede cuando hay dos etiquetas, dos loops y la sentencia continue?

En Javascript

outerLoop: for (let i = 0; i < 3; i++) {
  innerLoop: for (let j = 0; j < 3; j++) {
    if (i === 1 && j === 1) {

/* Al ejecutarse 'continue' se omite el resto del bucle 
interno y se pasa a la siguiente iteración 
del bucle exterior. */
      continue outerLoop;
    }
    console.log(`i: ${i}, j: ${j}`);
  }
}
/* Resultado similar al anterior 
pero con otros valores*/

El objetivo es pedirle al sistema, por ejemplo, que interrumpa o no una iteración identificable mediante un nombre. El ejemplo de abajo sería un bucle infinito sin el break correspondiente:

// Etiqueta identificadora de bucle
detenerEsto:

/* Bucle inicialmente infinito */
while (true){
console.log("Solo una vez.");
// Detener a la primeta iteración
break detenerEsto;
}
//-> Solo una vez.

let numeracion = '';

// Palabra etiqueta y Bucle FOR
repetirEsto:
for (let i = 0; i < 10; i++) {

// Condicional que evalua valor
  if (i === 1) {
// Llamar a ese bloque
    continue repetirEsto;
  }
// Actualizar el dato
  numeracion = numeracion + i + " ";
}

console.log(numeracion);
//-> 023456789


DECLARACIONES CONDICIONALES

Evalúan condición de forma binaria. El valor que se revisa es el de true –para ejecutar declaración en seguidilla– o false –obvia una declaración en seguidilla y va por la siguiente–. Las declaraciones condicionales pueden anidarse.

IF/ELSE
var a = 1, 
b = 1, 
c = 1, 
d = 1;

/* Si es true, siga al siguiente */
if (a == b){
/* Si es true, siga al siguiente */
if (b === c){
/* Si es true, siga al siguiente */
if (c === d){
/* Si es true, ejecute */
console.log(a + " es igual a " + 
c + "\n" + 
"El primero es igual al último");
}  /* Si es false, salte y ejecute aquí */
} else {
console.log("No todos son iguales");
}    
}
/* --> 1 es igual a 1.
El primero es igual al último */

// Una sola condición no necesita {..}
if (2 < 3) console.log(" 2 es menor a 3");
// --> 2 es menor a 3

Otro ejemplo que combina funciones, condicional if, y string literal templates. Estos últimos permiten obtener el valor de una variable, convertirlo directamente a string para mostrarlo.


/* factor --> Cantidad requerida de latas */
 const receta = function(factor) {

// Nueva función
/* Los tres argumentos */
   const valores = function(cantidad, unidad, nombre) {

// Sentencia con variable que guarda y opera
/* Guarda factor por cantidad*/
     let cantidadvalores = cantidad * factor;

     if (cantidadvalores > 1) {
/* Agrega 's' a palabra 'unidad' */
unidad += "s"; 
// Cierra If
}
/* Determina posición para 
mostrar el valor de las variables */
// --> Ejemplo de string literal template
// Si se separan, se mostrarán separados
console.log( `${cantidadvalores} ${"-->"} ${unidad} ${nombre}` );
// Cierra función 
};

// = = = = >
/* PILAS CON ESTO: */
/* DENTRO DE LA FUNCIÓN */
/* Nutrimos con diferentes valores 
a los argumentos de 'valores' */

// VALORES UNITARIOS
// cantidad, unidad, nombre
valores(1, "lata", "garbanzos"); 
valores(0.25, "taza", "tahini"); 
valores(0.25, "taza", "jugo de limón");
valores(1, "clavo", "ajo");
valores(2, "cucharada", "aceite de oliva"); 
valores(0.5, "cucharadita", "comino");
};// Cierra variable

/* Invocamos y nutrimos al argumento 
de 'receta' el factor, que es 
la cantidad de latas 
que deseamos calcular */

/* Para tantas latas, 
se necesitarán*/
receta(9, "", "");

Subir al inicio Avanzar



BIBLIOGRAFÍA:
    3ra edición. 2018. Marijn Haverbeke. No starch press.
    Mozilla Foundation.
    David Flanagan (2020). Editorial O'Reilly.
    Ved Antani, Stoyan Stefanov (2017). Packt Publishing.