#Javascript, #DOMJs, #webinteractiva, #Interfaces, #duduromeroa, #Guayaquil
Javascript
Programación para web (3/8): tipos de funciones, bloques de funciones, ámbitos; condicionales.
Por Eduardo Romero
| Guayaquil
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:
- Parámetros: Nombres identificadores que reciben datos desde la función.
- Argumentos: Datos necesarios insertados desde fuera de la función y para cada parámetro. Cada argumento puede ser invocado y mostrado.
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
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 ChatGPT sus utilidades son:
- Crear funciones que se ejecuten apenas son declaradas. Es decir, que no necesiten ser invocadas para activarse.
- Proteger código que permita cierre de función –closure– y así crear un nuevo alcance evitando colisiones entre variables.
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.
- RECORDAR SINTAXIS DE INCREMENTO
- x++ | x-- da el valor antes de incrementar/decrementar
- ++x | --x da el valor después del incremento/decremento
// 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
- Para resolver un problema complejo diviéndolo en pequeños subproblemas.
- Calcular secuencias como la Fibonnaci o factoriales
- En algoritmos de búsqueda
- En arreglos de alfanuméricos
DESEMPEÑO DE FUNCIONES RECURSIVAS
Para Svekis, Putten[5] y el ChatGPT 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 ChatGPT 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:
- Parámetros: es el identificador –nombre– de un valor, contenido entre paréntesis como una variable en un contexto único, como una función. Este parámetro podrá recibir un valor llamado argumento.
- Argumento: es un valor/objeto –numérico, alfanumérico, literal– ingresado en el parámetro. Ese valor será llamando cuando el contexto –una función, por ejemplo- sea invocada. Los argumentos estan rodeados por paréntesis ().
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 Juan Andrés Núñez [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".
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 ChatGPT:
/* ¿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 w3schools-javascript 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 w3schools-javascript 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 Héctor de León en hdeleon.net –canal de YouTube– 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 Héctor de León en su canal hdeleon.net de YouTube, 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 ChatGPT "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
- RECORDAR
- La función padre NO tiene acceso a los valores de la función hijo.
- La función hijo SÍ TIENE acceso a los valores de la función padre.
- Es decir, la cadena de alcance es desde dentro –desde el hijo– hacia afuera –hacia el padre-.
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 mdn web docs_, "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, mdn web docs_, aclara: anidar funciones dentro de otras funciones afecta el rendimiento del código y el consumo de memoria.
- ¿PORQUÉ LAS FUNCIONES ANIDADAS PUEDEN AFECTAR EL RENDIMIENTO EN JAVASCRIPT SEGÚN ChatGPT?
- Incremento en memoria: Cada función en JS es un objeto, por lo que funciones anidadas son objetos dentro de objetos en memoria o cuando se retiene referencias hacia la función padre.
- Incrementa pila de llamados: Cuando una función es llamada esa información es añadida a la pila de llamadas. Funciones anidades agranda esa pila además que se crea niveles más profundos de anidamiento y datos pasados entre funciones.
- Mayor tiempo de conversión de datos –parsing–: JS convierte y compila el código antes de ser ejecutado. Con funciones anidadas se alarga el tiempo de proceso.
- BENEFICIOS DE FUNCIONES ANIDADAS
- Modularidad: el código se estructura por niveles de ejecución.
- Privacidad de datos: valores internos no pueden ser accedidos desde funciones externas; solo desde su propio ámbito local.
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
osuper
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:
- A) Valor de espacio de 'enter' entre caracteres
- B) 'horizontal' es contador horizontal
- C) Primer FOR incrementa su valor a una unidad siempre que este entre 1 hasta 7.
- D) 'vertical' es contador vertical: Segundo FOR, siempre que este entre 1 y 15, incrementa en 1
- Ojo con operador condicional ternario:
res = res + ((i * j) % 8 ? ' ' : '*');
- E) Valor 'res' se actualiza en cada repetición y se activa operador ternario, que evaluará la multiplicación entre valores horizontal y vertical, luego, ese resultado se dividirá para 8. Si el residuo de la división es true (o sea, con un valor mayor a 1) se agregará un espacio; si el residuo es false (cero) se agregará un asterico.
- F) Finalmente, el valor de 'res' se actualiza de nuevo: en cada repetición será igual a un nuevo espacio de 'enter'.
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. Flanagan[3] dice: "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