Módulo 04: Estructuras de control
Objetivos del módulo
- Entender qué es el flujo de ejecución y cómo se controla con bloques
- Tomar decisiones con
if,else ifyelse, y saber cuándo usar el ternario - Usar
switchcorrectamente:break, fallthrough,defaulty el patrónswitch (true) - Sustituir
switchpor objetos de búsqueda cuando sea más claro - Repetir código con
while,do...while,for,for...ofyfor...in - Saber por qué
for...inno se usa con arrays - Controlar los bucles con
break,continuey etiquetas, y evitar los bucles infinitos - Construir programas completos: FizzBuzz, una tabla de multiplicar en la página y el juego “adivina el número” en el navegador y en Node.js
- Conocer el early return y las guard clauses para escribir código más plano
1. El flujo de ejecución y los bloques
📘 Concepto: Por defecto, JavaScript ejecuta las instrucciones de arriba abajo, una detrás de otra. Las estructuras de control permiten cambiar ese orden: decidir qué código se ejecuta (condicionales) o repetir un código varias veces (bucles). Son como las bifurcaciones y las rotondas de una carretera.
Un bloque es un grupo de instrucciones entre llaves { }. Las estructuras de control trabajan con bloques, y las variables declaradas con let o const dentro de un bloque solo existen dentro de él:
{
const mensaje = 'Dentro del bloque';
console.log(mensaje); // Dentro del bloque
}
// console.log(mensaje); ❌ ReferenceError: mensaje is not defined
💡 Consejo: Sangra (indenta) el contenido de cada bloque con 2 espacios. No cambia cómo se ejecuta el código, pero hace evidente qué está dentro de qué. Prettier lo hará por ti al guardar.
2. Condicionales: if, else if y else
2.1 if
Ejecuta un bloque solo si la condición es verdadera (truthy):
const temperatura = 32;
if (temperatura > 30) {
console.log('Hace mucho calor. ¡Bebe agua!');
}
console.log('Fin del programa'); // esto se ejecuta siempre
2.2 if...else
const edad = 16;
if (edad >= 18) {
console.log('Puedes votar');
} else {
console.log(`Podrás votar dentro de ${18 - edad} años`);
}
// Podrás votar dentro de 2 años
2.3 else if: varias alternativas
Las condiciones se comprueban en orden y solo se ejecuta el primer bloque cuya condición se cumpla:
const nota = 7.5;
let calificacion;
if (nota < 0 || nota > 10) {
calificacion = 'Nota no válida';
} else if (nota < 5) {
calificacion = 'Suspenso';
} else if (nota < 7) {
calificacion = 'Aprobado';
} else if (nota < 9) {
calificacion = 'Notable';
} else {
calificacion = 'Sobresaliente';
}
console.log(calificacion); // Notable
flowchart TD
A["nota"] --> B{"¿nota < 0 o > 10?"}
B -->|Sí| X["Nota no válida"]
B -->|No| C{"¿nota < 5?"}
C -->|Sí| S["Suspenso"]
C -->|No| D{"¿nota < 7?"}
D -->|Sí| AP["Aprobado"]
D -->|No| E{"¿nota < 9?"}
E -->|Sí| N["Notable"]
E -->|No| SB["Sobresaliente"]
⚠️ Importante: El orden de las condiciones importa. Si pusieras primero
nota < 9, un 3 entraría ahí y nunca llegaría a “Suspenso”. Ordena de lo más restrictivo a lo más general.
2.4 Condiciones compuestas y truthy/falsy
Puedes combinar condiciones con &&, || y ! (módulo 03) y aprovechar los valores truthy/falsy (módulo 02):
const usuario = 'ana';
const contrasena = '';
if (!usuario || !contrasena) {
console.log('Rellena usuario y contraseña'); // se ejecuta: contrasena es ''
}
const hora = 15;
const esFinDeSemana = false;
if (hora >= 9 && hora < 18 && !esFinDeSemana) {
console.log('La tienda está abierta');
}
2.5 Usa siempre llaves
JavaScript permite omitir las llaves si el bloque tiene una sola instrucción, pero es fuente de errores:
const saldo = 50;
// ❌ Parece que las dos líneas están dentro del if, pero solo la primera lo está
if (saldo < 0)
console.log('Saldo negativo');
console.log('Se ha enviado un aviso al banco'); // ¡se ejecuta SIEMPRE!
// ✅ Con llaves no hay dudas
if (saldo < 0) {
console.log('Saldo negativo');
console.log('Se ha enviado un aviso al banco');
}
💡 Consejo: Escribe siempre las llaves. ESLint tiene la regla
curlypara exigirlo.
3. Ternario frente a if
El operador ternario (módulo 03) es una expresión que devuelve un valor; if es una instrucción que ejecuta código. Elige según lo que necesites:
| Situación | Usa | Ejemplo |
|---|---|---|
| Elegir entre dos valores | Ternario | const texto = stock > 0 ? 'Disponible' : 'Agotado'; |
| Valor dentro de una plantilla | Ternario | `${n} ${n === 1 ? 'año' : 'años'}` |
| Ejecutar acciones distintas | if/else | Guardar, mostrar mensajes, llamar a funciones |
| Más de dos alternativas | if/else if o un objeto de búsqueda | Calificaciones, tarifas por tramos |
const puntos = 120;
// ✅ Ternario: elegimos un valor
const nivel = puntos >= 100 ? 'Oro' : 'Plata';
// ✅ if: ejecutamos acciones
if (puntos >= 100) {
console.log('¡Enhorabuena! Has subido de nivel');
console.log('Te enviamos un regalo por correo');
}
// ❌ Ternario usado para ejecutar acciones: funciona, pero se lee mal
puntos >= 100 ? console.log('Oro') : console.log('Plata');
4. switch
switch compara un valor con una lista de casos posibles. Es una alternativa a una cadena de else if cuando todas las condiciones comparan la misma variable con valores concretos.
4.1 Sintaxis
const metodoPago = 'bizum';
switch (metodoPago) {
case 'tarjeta':
console.log('Introduce los datos de tu tarjeta');
break;
case 'bizum':
console.log('Introduce tu número de teléfono');
break;
case 'transferencia':
console.log('Te enviaremos el IBAN por correo');
break;
default:
console.log('Método de pago no válido');
}
// Introduce tu número de teléfono
| Parte | Función |
|---|---|
switch (valor) | La expresión que se va a comparar |
case x: | Si valor === x, empieza a ejecutar desde aquí |
break | Sale del switch |
default: | Se ejecuta si ningún case coincide (opcional, normalmente al final) |
⚠️ Importante:
switchcompara con igualdad estricta (===). Si el valor es el texto'2'y el caso es el número2, no coinciden. Cuidado con los datos que vienen deprompto de formularios, que siempre son texto.
4.2 El fallthrough: cuando olvidas el break
Si un case no termina en break, la ejecución continúa en el siguiente case sin comprobarlo. Es el llamado fallthrough (“caer a través”):
const nivel = 2;
switch (nivel) {
case 1:
console.log('Nivel 1');
case 2:
console.log('Nivel 2');
case 3:
console.log('Nivel 3');
default:
console.log('Fin');
}
// Nivel 2
// Nivel 3 ← ¡se ha "caído" al siguiente caso!
// Fin
Casi siempre es un error, pero se puede usar a propósito para agrupar casos que comparten el mismo código:
function tipoDia(dia) {
switch (dia) {
case 'sábado':
case 'domingo':
return 'fin de semana';
case 'lunes':
case 'martes':
case 'miércoles':
case 'jueves':
case 'viernes':
return 'laborable';
default:
return 'día no válido';
}
}
console.log(tipoDia('domingo')); // fin de semana
console.log(tipoDia('martes')); // laborable
console.log(tipoDia('festivo')); // día no válido
📘 Concepto: Dentro de una función,
returnsale de la función entera, así que hace innecesario elbreak. Verás las funciones en el módulo 05.
4.3 Variables dentro de un case
Todos los case comparten el mismo bloque, así que declarar la misma variable en dos casos da error. La solución es poner llaves:
const accion = 'sumar';
switch (accion) {
case 'sumar': {
const resultado = 2 + 3;
console.log(resultado);
break;
}
case 'restar': {
const resultado = 2 - 3; // ✅ sin llaves daría SyntaxError: ya declarada
console.log(resultado);
break;
}
}
4.4 El patrón switch (true)
Para comparar rangos con switch, se usa un truco: comparar true con condiciones booleanas. Se ejecuta el primer case cuya condición sea true:
const imc = 27.3;
switch (true) {
case imc < 18.5:
console.log('Bajo peso');
break;
case imc < 25:
console.log('Peso normal');
break;
case imc < 30:
console.log('Sobrepeso'); // ← este
break;
default:
console.log('Obesidad');
}
💡 Consejo:
switch (true)es legal y se ve en código real, pero muchos equipos consideran más claro unif/else ifpara rangos. Úsalo si a tu equipo le resulta legible.
5. Objetos de búsqueda en lugar de switch
Muchos switch solo sirven para traducir un valor en otro. En esos casos, un objeto literal usado como tabla de búsqueda (lookup table) es más corto, más fácil de ampliar y separa los datos de la lógica:
// ❌ Con switch: mucho código repetido
function precioBebidaSwitch(bebida) {
switch (bebida) {
case 'cafe':
return 1.2;
case 'te':
return 1.5;
case 'zumo':
return 2.3;
default:
return null;
}
}
// ✅ Con un objeto de búsqueda
const PRECIOS = {
cafe: 1.2,
te: 1.5,
zumo: 2.3,
};
const bebida = 'te';
console.log(PRECIOS[bebida] ?? 'No disponible'); // 1.5
console.log(PRECIOS.chocolate ?? 'No disponible'); // No disponible
📘 Concepto: Con
objeto[variable]accedes a la propiedad cuyo nombre está guardado en la variable. Si no existe, obtienesundefined, y con??das un valor por defecto. Lo verás a fondo en el módulo 07.
También puedes guardar funciones como valores, para ejecutar acciones distintas:
const operaciones = {
sumar: (a, b) => a + b,
restar: (a, b) => a - b,
multiplicar: (a, b) => a * b,
};
const op = 'multiplicar';
const calcular = operaciones[op];
if (calcular) {
console.log(calcular(6, 7)); // 42
} else {
console.log('Operación desconocida');
}
⚠️ Importante: Un objeto normal “hereda” propiedades como
toString, así quePRECIOS['toString']no esundefined(es una función). Si las claves vienen del usuario, comprueba conObject.hasOwn(PRECIOS, clave)o usa unMap(módulo 11).
6. El bucle while
📘 Concepto: Un bucle repite un bloque de código mientras se cumpla una condición. Cada repetición se llama iteración.
whilecomprueba la condición antes de cada vuelta, así que si es falsa desde el principio, el bloque no se ejecuta nunca.
let cuenta = 3;
while (cuenta > 0) {
console.log(cuenta);
cuenta--; // ¡imprescindible! Si no, la condición nunca cambia
}
console.log('¡Despegue!');
// 3
// 2
// 1
// ¡Despegue!
while es ideal cuando no sabes de antemano cuántas vueltas darás:
// ¿Cuántos años tarda en duplicarse un ahorro al 3 % anual?
let capital = 1000;
let anios = 0;
while (capital < 2000) {
capital *= 1.03;
anios++;
}
console.log(`Se duplica en ${anios} años`); // Se duplica en 24 años
7. El bucle do...while
Igual que while, pero comprueba la condición después de cada vuelta. Por eso el bloque se ejecuta al menos una vez:
let n = 0;
do {
n++;
} while (n < 0); // la condición es falsa desde el principio...
console.log(n); // 1 ← ...pero el bloque se ejecutó una vez
Su uso típico es pedir un dato hasta que sea válido (en el navegador):
let edad;
do {
edad = Number(prompt('¿Cuántos años tienes?'));
} while (!Number.isInteger(edad) || edad < 0);
console.log(`Tienes ${edad} años`);
| Bucle | Comprueba la condición | Mínimo de ejecuciones |
|---|---|---|
while | Antes de cada vuelta | 0 |
do...while | Después de cada vuelta | 1 |
8. El bucle for clásico
Cuando sabes cuántas veces quieres repetir algo, for reúne en una sola línea las tres partes de un bucle con contador:
for (inicialización; condición; actualización) {
// cuerpo
}
for (let i = 1; i <= 5; i++) {
console.log(`Vuelta número ${i}`);
}
// Vuelta número 1 ... Vuelta número 5
| Parte | Cuándo se ejecuta | En el ejemplo |
|---|---|---|
| Inicialización | Una vez, al principio | let i = 1 |
| Condición | Antes de cada vuelta; si es falsa, termina | i <= 5 |
| Cuerpo | En cada vuelta | console.log(...) |
| Actualización | Al final de cada vuelta | i++ |
flowchart TD
A["Inicialización<br/>let i = 1"] --> B{"Condición<br/>i <= 5"}
B -->|true| C["Cuerpo"]
C --> D["Actualización<br/>i++"]
D --> B
B -->|false| E["Fin del bucle"]
Variantes habituales:
// Hacia atrás y de dos en dos
for (let i = 10; i >= 0; i -= 2) {
console.log(i); // 10, 8, 6, 4, 2, 0
}
// Recorrer un array por su índice
const ciudades = ['Madrid', 'Sevilla', 'Bilbao'];
for (let i = 0; i < ciudades.length; i++) {
console.log(`${i + 1}. ${ciudades[i]}`);
}
// 1. Madrid
// 2. Sevilla
// 3. Bilbao
⚠️ Importante: Los índices de un array van de
0alength - 1. La condición correcta esi < array.length, noi <= array.length(en el módulo 01 depuraste exactamente este error).
9. El bucle for...of
for...of (ES2015) recorre directamente los valores de cualquier iterable: arrays, cadenas, Map, Set… No necesitas índices ni length:
const frutas = ['manzana', 'pera', 'uva'];
for (const fruta of frutas) {
console.log(fruta);
}
// manzana
// pera
// uva
💡 Consejo: Fíjate en que usamos
const: en cada vuelta se crea una variable nueva con el valor correspondiente. Usaletsolo si necesitas reasignarla dentro del bucle.
Con cadenas de texto, recorre los caracteres (incluso los emojis, que ocupan dos “unidades” internas):
for (const letra of 'Hola 👋') {
console.log(letra); // H, o, l, a, ' ', 👋
}
console.log('Hola 👋'.length); // 7 ← length cuenta el emoji como 2
¿Y si necesitas también el índice? Usa entries():
const frutas = ['manzana', 'pera'];
for (const [indice, fruta] of frutas.entries()) {
console.log(indice, fruta);
}
// 0 manzana
// 1 pera
📘 Concepto:
[indice, fruta]es desestructuración: extrae los dos valores del par[0, 'manzana']en dos variables. La verás en detalle en los módulos 06 y 07.
10. El bucle for...in
for...in recorre las claves (nombres de propiedades) de un objeto:
const coche = { marca: 'Seat', modelo: 'Ibiza', anio: 2022 };
for (const clave in coche) {
console.log(`${clave}: ${coche[clave]}`);
}
// marca: Seat
// modelo: Ibiza
// anio: 2022
10.1 Por qué no usar for...in con arrays
const letras = ['a', 'b'];
letras.extra = 'x'; // los arrays son objetos: se les pueden añadir propiedades
for (const i in letras) {
console.log(i, typeof i);
}
// 0 string ← ¡los índices son TEXTOS, no números!
// 1 string
// extra string ← ¡recorre también propiedades que no son elementos!
Problema de for...in con arrays | Consecuencia |
|---|---|
| Las claves son strings | i + 1 da '01' en vez de 1 |
| Recorre propiedades añadidas y heredadas enumerables | Aparecen elementos “fantasma” |
| Está pensado para objetos | Es más lento y confuso con arrays |
⚠️ Importante: Regla fácil:
for...ofpara valores de arrays y cadenas;for...inpara claves de objetos. Y aun para objetos, el código moderno suele preferirfor (const [clave, valor] of Object.entries(obj)), que verás en el módulo 07.
10.2 Tabla comparativa de bucles
| Bucle | Recorre | Úsalo cuando |
|---|---|---|
while | Mientras se cumpla la condición | No sabes cuántas vueltas darás |
do...while | Ídem, al menos una vez | Pedir datos hasta que sean válidos |
for | Un contador | Necesitas el índice, saltos, o recorrer al revés |
for...of | Valores de un iterable | Recorrer arrays, cadenas, Map, Set (lo más habitual) |
for...in | Claves de un objeto | Recorrer propiedades de un objeto literal |
11. break, continue y etiquetas
11.1 break: salir del bucle
const numeros = [3, 8, 15, 4, 23, 42];
for (const numero of numeros) {
if (numero > 20) {
console.log(`Primer número mayor que 20: ${numero}`);
break; // dejamos de buscar
}
}
// Primer número mayor que 20: 23
11.2 continue: saltar a la siguiente vuelta
const movimientos = [4, -2, 7, 0, -5, 9];
let sumaPositivos = 0;
for (const m of movimientos) {
if (m <= 0) {
continue; // ignora este y pasa al siguiente
}
sumaPositivos += m;
}
console.log(sumaPositivos); // 20
11.3 Etiquetas: salir de bucles anidados
break y continue afectan solo al bucle más interno. Para salir de varios a la vez, pon una etiqueta (un nombre seguido de :) al bucle externo:
externo: for (let fila = 1; fila <= 3; fila++) {
for (let col = 1; col <= 3; col++) {
if (fila * col === 4) {
console.log(`Encontrado: ${fila} x ${col}`);
break externo; // sale de LOS DOS bucles
}
console.log(fila, col);
}
}
// 1 1
// 1 2
// 1 3
// 2 1
// Encontrado: 2 x 2
💡 Consejo: Las etiquetas son poco frecuentes. Si necesitas salir de varios bucles anidados, a menudo es más claro meter los bucles en una función y usar
return(sección 14).
12. Bucles infinitos y cómo evitarlos
Un bucle infinito es uno cuya condición nunca llega a ser falsa. En el navegador congela la pestaña; en Node.js, el programa no termina nunca (párralo con Ctrl + C).
// ❌ Olvidar actualizar la variable
let i = 0;
while (i < 5) {
console.log(i); // i siempre vale 0
}
// ❌ Actualizar en la dirección equivocada
for (let j = 0; j < 10; j--) { /* j cada vez más pequeño */ }
// ❌ Comparar decimales con igualdad exacta
let x = 0;
while (x !== 1) {
x += 0.1; // 0.1 + 0.1 + ... nunca da exactamente 1 (da 0.9999999999999999)
}
Cómo evitarlos:
| Causa | Solución |
|---|---|
| La variable de control no cambia | Comprueba que el cuerpo la actualiza en cada vuelta |
| Cambia en la dirección equivocada | Revisa ++ frente a -- y < frente a > |
| Comparación exacta con decimales | Usa < o <= en lugar de !== / === |
| Condiciones que dependen de datos externos | Añade un límite de seguridad (máximo de intentos) |
// ✅ Límite de seguridad
const MAX_VUELTAS = 1000;
let vueltas = 0;
let valor = 0;
while (valor !== 1 && vueltas < MAX_VUELTAS) {
valor += 0.1;
vueltas++;
}
if (vueltas === MAX_VUELTAS) {
console.warn('Se alcanzó el límite de vueltas: revisa la condición');
}
📘 Concepto: A veces se usa un bucle infinito a propósito, como
while (true) { ... }, con unbreakdentro cuando se cumple una condición. Es correcto siempre que elbreaksea alcanzable.
13. Ejemplos completos
13.1 FizzBuzz
Un clásico de las entrevistas de programación: recorre los números del 1 al 100; si el número es múltiplo de 3 escribe “Fizz”, si es múltiplo de 5 escribe “Buzz”, si es múltiplo de ambos “FizzBuzz” y, si no, el número.
// fizzbuzz.mjs
for (let i = 1; i <= 100; i++) {
if (i % 15 === 0) {
console.log('FizzBuzz'); // múltiplo de 3 y de 5 (es decir, de 15)
} else if (i % 3 === 0) {
console.log('Fizz');
} else if (i % 5 === 0) {
console.log('Buzz');
} else {
console.log(i);
}
}
// 1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz, 16...
⚠️ Importante: La comprobación de
15va primero. Si comprobaras antesi % 3, el 15 entraría ahí y escribiría “Fizz” en lugar de “FizzBuzz”.
13.2 Tabla de multiplicar en la página
Por primera vez vas a crear contenido en la página desde JavaScript. Usaremos dos herramientas sencillas que estudiarás a fondo en el módulo 14: document.createElement() crea un elemento y document.body.append() lo añade al final de la página.
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tabla de multiplicar</title>
<style>
body { font-family: system-ui, sans-serif; margin: 2rem; }
p { margin: 0.25rem 0; font-size: 1.2rem; }
.destacado { font-weight: bold; color: rebeccapurple; }
</style>
<script type="module">
const numero = Number(prompt('¿Qué tabla quieres ver? (1-10)', '7'));
if (!Number.isInteger(numero) || numero < 1 || numero > 10) {
document.body.append('Número no válido. Recarga la página e inténtalo de nuevo.');
} else {
const titulo = document.createElement('h1');
titulo.textContent = `Tabla del ${numero}`;
document.body.append(titulo);
for (let i = 1; i <= 10; i++) {
const linea = document.createElement('p');
linea.textContent = `${numero} × ${i} = ${numero * i}`;
if (i % 2 === 0) {
linea.classList.add('destacado'); // resaltamos las filas pares
}
document.body.append(linea);
}
}
</script>
</head>
<body>
</body>
</html>
💡 Consejo:
appendtambién acepta texto directamente (como en el mensaje de error). Y usamostextContenten lugar deinnerHTMLpara escribir texto: es más seguro, porque nunca interpreta etiquetas HTML (lo verás en el módulo 14).
13.3 Adivina el número (navegador)
El ordenador piensa un número del 1 al 100 y tú tienes 7 intentos para adivinarlo. Esta versión usa prompt y alert, que bloquean la página mientras esperan respuesta: sirven para practicar, pero no se usan en webs reales (allí usarás formularios y eventos, módulos 15 y 16).
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Adivina el número</title>
<script type="module">
const MAX_INTENTOS = 7;
const secreto = Math.floor(Math.random() * 100) + 1; // entero entre 1 y 100
let intentos = 0;
let acertado = false;
while (intentos < MAX_INTENTOS && !acertado) {
const respuesta = prompt(`Intento ${intentos + 1}/${MAX_INTENTOS}. Escribe un número del 1 al 100:`);
if (respuesta === null) {
break; // el usuario ha pulsado "Cancelar"
}
const numero = Number(respuesta);
if (!Number.isInteger(numero) || numero < 1 || numero > 100) {
alert('Escribe un número entero entre 1 y 100.');
continue; // no cuenta como intento
}
intentos++;
if (numero === secreto) {
acertado = true;
} else if (numero < secreto) {
alert('Más alto ⬆️');
} else {
alert('Más bajo ⬇️');
}
}
const mensaje = acertado
? `¡Correcto! Era el ${secreto}. Lo has conseguido en ${intentos} intentos.`
: `Fin del juego. El número era el ${secreto}.`;
alert(mensaje);
document.body.append(mensaje);
</script>
</head>
<body>
</body>
</html>
📘 Concepto:
Math.random()devuelve un decimal aleatorio entre 0 (incluido) y 1 (excluido). Multiplicado por 100 da un valor entre 0 y 99,999…;Math.floorlo deja en un entero de 0 a 99, y el+ 1lo desplaza al rango 1-100.
13.4 Adivina el número (Node.js con readline)
En Node.js no existe prompt. Para leer lo que escribe el usuario en la terminal se usa el módulo node:readline/promises. Su método question() muestra una pregunta y espera la respuesta; como esa espera es asíncrona, se usa await (módulo 12). En un módulo ES puedes usar await directamente en el nivel superior.
// adivina.mjs
import * as readline from 'node:readline/promises';
import { stdin as input, stdout as output } from 'node:process';
const rl = readline.createInterface({ input, output });
const MAX_INTENTOS = 7;
const secreto = Math.floor(Math.random() * 100) + 1;
let intentos = 0;
let acertado = false;
console.log('He pensado un número del 1 al 100. ¡Adivínalo!');
while (intentos < MAX_INTENTOS && !acertado) {
const respuesta = await rl.question(`Intento ${intentos + 1}/${MAX_INTENTOS}: `);
const numero = Number(respuesta);
if (!Number.isInteger(numero) || numero < 1 || numero > 100) {
console.log('Escribe un número entero entre 1 y 100.');
continue;
}
intentos++;
if (numero === secreto) {
acertado = true;
} else if (numero < secreto) {
console.log('Más alto ⬆️');
} else {
console.log('Más bajo ⬇️');
}
}
if (acertado) {
console.log(`¡Correcto! Era el ${secreto}. Lo has conseguido en ${intentos} intentos.`);
} else {
console.log(`Has agotado los intentos. El número era el ${secreto}.`);
}
rl.close(); // importante: si no cierras la interfaz, el programa no termina
PS C:\cursos> node adivina.mjs
He pensado un número del 1 al 100. ¡Adivínalo!
Intento 1/7: 50
Más bajo ⬇️
Intento 2/7: hola
Escribe un número entero entre 1 y 100.
Intento 2/7: 25
Más alto ⬆️
Intento 3/7: 42
¡Correcto! Era el 42. Lo has conseguido en 3 intentos.
💡 Consejo: Compara las dos versiones: la lógica del juego (el bucle, las comprobaciones, los contadores) es idéntica. Solo cambia cómo se pide y se muestra la información. Separar la lógica de la entrada/salida es una idea clave que aprovecharás cuando conozcas las funciones.
14. Early return y guard clauses (adelanto)
Cuando hay muchas condiciones, el código tiende a anidarse en forma de “flecha” y se vuelve difícil de seguir:
// ❌ Código en "flecha": cada condición añade un nivel de sangría
function procesarPedido(pedido) {
if (pedido) {
if (pedido.productos.length > 0) {
if (pedido.pagado) {
console.log('Enviando pedido...');
} else {
console.log('El pedido no está pagado');
}
} else {
console.log('El pedido está vacío');
}
} else {
console.log('No hay pedido');
}
}
Una guard clause (cláusula de guarda) comprueba los casos problemáticos al principio y sale de la función con return (early return, retorno temprano). Lo que queda al final es el “camino feliz”, sin anidar:
// ✅ Con guard clauses: plano y legible
function procesarPedido(pedido) {
if (!pedido) {
console.log('No hay pedido');
return;
}
if (pedido.productos.length === 0) {
console.log('El pedido está vacío');
return;
}
if (!pedido.pagado) {
console.log('El pedido no está pagado');
return;
}
console.log('Enviando pedido...'); // el camino feliz
}
procesarPedido({ productos: ['libro'], pagado: true }); // Enviando pedido...
procesarPedido({ productos: [], pagado: true }); // El pedido está vacío
📘 Concepto: La misma idea funciona en los bucles con
continue: descarta pronto los elementos que no te interesan y deja el código importante sin anidar. Profundizarás enreturnen el módulo 05.
15. Ejercicios
Ejercicio 1: Tarifa del cine
Escribe un programa que, a partir de una constante edad y otra dia (por ejemplo, 'miércoles'), calcule el precio de la entrada: 4 € para menores de 12 años, 5 € para mayores de 65 y 8 € para el resto. Los miércoles hay un 50 % de descuento para todos. Usa if/else if/else y muestra el precio con toFixed(2).
Ejercicio 2: Calculadora con switch y con objeto
Con las constantes a, b y operador ('+', '-', '*', '/', '%', '**'), calcula el resultado primero con un switch (controla la división entre cero y el operador no válido con default). Después reescríbelo usando un objeto de búsqueda con funciones flecha y compara ambas versiones.
Ejercicio 3: Números primos
Muestra todos los números primos entre 2 y 100. Un número es primo si solo es divisible entre 1 y él mismo. Pista: para cada número, usa un bucle interno que pruebe divisores desde 2 y un break en cuanto encuentres uno. Optimización: basta con probar divisores hasta la raíz cuadrada del número (d * d <= n).
Ejercicio 4: Estadísticas de un array
Dado const temperaturas = [12.5, 15, 9.8, 21.3, 18, 7.2, 14.6];, calcula con un for...of la temperatura máxima, la mínima y la media, y cuenta cuántos días superaron los 15 grados. Muestra los resultados con console.table.
Ejercicio 5: Pirámide de asteriscos
Pide (con prompt en el navegador o con readline en Node) una altura n y dibuja una pirámide centrada con asteriscos. Para n = 4:
*
***
*****
*******
Pista: en la fila i (empezando en 1) hay n - i espacios y 2 * i - 1 asteriscos. Puedes usar ' '.repeat(cantidad).
Ejercicio 6: Tabla de multiplicar completa en el DOM
Amplía el ejemplo 13.2 para que muestre todas las tablas del 1 al 10, cada una con su título h2, usando dos bucles for anidados. Resalta con una clase CSS los resultados que sean múltiplos de 10.
Ejercicio 7: Cajero automático
Crea en Node.js (con readline) un menú que se repita hasta que el usuario elija “Salir”: 1) Consultar saldo, 2) Ingresar, 3) Retirar, 4) Salir. El saldo empieza en 500 €. No se puede retirar más de lo que hay ni cantidades negativas. Usa do...while o while (true) con break, y switch para las opciones.
Ejercicio 8: Mejora el juego
Mejora “Adivina el número” (cualquiera de las dos versiones): al terminar, pregunta si se quiere jugar otra vez; guarda el mejor resultado (menor número de intentos) entre partidas; y añade niveles de dificultad (fácil: 1-50 con 10 intentos; normal: 1-100 con 7; difícil: 1-500 con 9) elegidos con un objeto de búsqueda.
Resumen
| Estructura | Sintaxis | Úsala para |
|---|---|---|
if | if (cond) { ... } | Ejecutar código si se cumple una condición |
if/else if/else | if (a) {} else if (b) {} else {} | Varias alternativas (orden importante) |
| Ternario | cond ? valor1 : valor2 | Elegir entre dos valores |
switch | switch (x) { case 1: ... break; default: ... } | Comparar un valor con casos concretos (===) |
switch (true) | case x < 10: | Rangos (alternativa a else if) |
| Objeto de búsqueda | TABLA[clave] ?? defecto | Traducir valores sin switch |
while | while (cond) { ... } | Repetir sin saber cuántas veces |
do...while | do { ... } while (cond); | Repetir al menos una vez |
for | for (let i = 0; i < n; i++) { ... } | Repetir con contador o índice |
for...of | for (const v of iterable) { ... } | Valores de arrays y cadenas |
for...in | for (const k in objeto) { ... } | Claves de objetos (no arrays) |
break | break; / break etiqueta; | Salir del bucle |
continue | continue; | Saltar a la siguiente vuelta |
| Guard clause | if (!dato) { return; } | Salir pronto y evitar anidar |