Lección 3 de 8. Decidir y repetir en JavaScript: if, switch y los bucles se parecen a los de cualquier lenguaje con llaves, pero cada uno tiene su detalle. Y al final, los tres operadores modernos que más código ahorran: ??, ?. y ??=.
Decidir: if, else y el ternario
El if de JavaScript se escribe como en C o en Java: la condición entre paréntesis y el bloque entre llaves. Lo que entra en la condición no tiene por qué ser un booleano: se aplican las reglas de valores verdaderos y falsos de la lección 2.
function calificacion(nota) {
if (nota >= 9) {
return "sobresaliente";
} else if (nota >= 7) {
return "notable";
} else if (nota >= 5) {
return "aprobado";
} else {
return "suspenso";
}
}
for (const n of [9.5, 7, 5, 3.2]) {
console.log(n, "->", calificacion(n));
}salida9.5 -> sobresaliente 7 -> notable 5 -> aprobado 3.2 -> suspenso
Cuando solo hay que elegir entre dos valores, el operador ternario condición ? si : no lo dice en una línea. Úsalo para valores, no para encadenar decisiones: tres ternarios anidados se leen peor que el if de arriba.
const stock = 0;
const etiqueta = stock > 0 ? `quedan ${stock}` : "agotado";
console.log(etiqueta);salidaagotado
switch, y la caída que nadie espera
switch compara un valor con varios casos. Tiene una particularidad heredada de C: cuando entra en un caso, sigue ejecutando los siguientes hasta encontrar un break. Si se te olvida, pasa esto:
function plan(dia) {
const tareas = [];
switch (dia) {
case "lunes":
tareas.push("reunión");
case "martes":
tareas.push("código");
case "miércoles":
tareas.push("revisión");
break;
default:
tareas.push("descanso");
}
return tareas;
}
console.log(plan("martes"));
console.log(plan("domingo"));salida[ 'código', 'revisión' ] [ 'descanso' ]
break; solo se detiene al llegar al break del miércoles. Esquema: decodigo.com.A veces la caída se usa a propósito, para que varios casos compartan el mismo código. Es legítimo si los casos van seguidos y sin nada en medio:
function tipoDia(dia) {
switch (dia) {
case "sábado":
case "domingo":
return "fin de semana";
default:
return "laborable";
}
}
console.log(tipoDia("domingo"), "|", tipoDia("lunes"));salidafin de semana | laborable
===. Un "1" que llega de un formulario no entra en case 1:, porque una cadena nunca es estrictamente igual a un número. Convierte antes con Number(valor).const valor = "1"; // lo que llega de un <input>
switch (valor) {
case 1:
console.log("uno");
break;
default:
console.log("ningún caso:", typeof valor);
}salidaningún caso: string
Bucles
for, while y do…while
El for clásico tiene tres partes: inicio, condición y paso. Úsalo cuando necesites el índice o un paso distinto de uno; para recorrer una colección hay algo mejor, justo debajo.
for (let i = 10; i > 0; i -= 3) {
console.log("cuenta atrás:", i);
}
let intentos = 0;
while (intentos < 3) {
intentos++;
}
console.log("intentos:", intentos);salidacuenta atrás: 10 cuenta atrás: 7 cuenta atrás: 4 cuenta atrás: 1 intentos: 3
do...while es como while, pero comprueba la condición al final, así que el cuerpo se ejecuta al menos una vez. Se usa poco: para pedir un dato hasta que sea válido, más o menos.
for…of frente a for…in
Son los dos que más se confunden, y la diferencia es sencilla: for...of recorre valores; for...in, claves.
const lista = ["pan", "leche", "café"];
lista.extra = "¿y esto?";
for (const v of lista) console.log("of:", v);
for (const k in lista) console.log("in:", k, typeof k);salidaof: pan of: leche of: café in: 0 string in: 1 string in: 2 string in: extra string
for...of da los valores del array. for...in da las claves, como cadenas, y además cualquier propiedad que alguien haya añadido. Esquema: decodigo.com.Dos detalles explican por qué for...in no sirve para arrays: las claves llegan como texto ("0" + 1 da "01", no 1) y aparecen propiedades que no son elementos. Para un array, for...of; y si necesitas también el índice, entries():
const lista = ["pan", "leche", "café"];
for (const [i, v] of lista.entries()) {
console.log(i + 1, v);
}
const precios = { pan: 1.2, leche: 0.95 };
for (const [producto, precio] of Object.entries(precios)) {
console.log(`${producto}: ${precio} USD`);
}salida1 pan 2 leche 3 café pan: 1.2 USD leche: 0.95 USD
Para los objetos, Object.entries() con for...of es la forma más clara: da cada par clave y valor, y los corchetes de const [producto, precio] los separan en dos variables. Es la desestructuración, que se ve a fondo en la lección 5. for...of también recorre cadenas, letra a letra, y los Map y Set.
break, continue y etiquetas
break sale del bucle y continue salta a la siguiente vuelta. Los dos actúan sobre el bucle más interno. Para salir de dos bucles anidados a la vez, se le pone una etiqueta al de fuera:
const tablero = [
[0, 0, 0],
[0, 7, 0],
[0, 0, 7],
];
buscar: for (let f = 0; f < tablero.length; f++) {
for (let c = 0; c < tablero[f].length; c++) {
if (tablero[f][c] === 0) continue;
console.log(`primer 7 en la fila ${f}, columna ${c}`);
break buscar;
}
}salidaprimer 7 en la fila 1, columna 1
Sin la etiqueta, el break solo saldría del bucle de las columnas y la búsqueda seguiría por la fila siguiente, encontrando también el segundo 7. Si te ves usando muchas etiquetas, suele ser señal de que ese trozo debería ser una función con un return.
&& y || devuelven valores, no booleanos
En JavaScript, a || b no devuelve true o false: devuelve a si es verdadero y, si no, b. Y a && b devuelve a si es falso y, si no, b. Los dos se detienen en cuanto saben el resultado, así que la parte derecha puede no llegar a ejecutarse. Por eso se han usado siempre para poner valores por omisión:
const nombre = "";
console.log(nombre || "anónimo");
const usuario = { admin: true };
usuario.admin && console.log("mostrar el panel de administración");
const puerto = 0; // 0 es un puerto válido aquí
console.log("puerto:", puerto || 8080);salidaanónimo mostrar el panel de administración puerto: 8080
La última línea es el problema: el usuario eligió el puerto 0 y || lo cambió por 8080, porque 0 es un valor falso. Lo mismo pasa con una cadena vacía o con false que sí querías conservar. Para esto existe un operador más preciso.
?? : solo null y undefined
El operador ?? (fusión de nulos) devuelve la parte derecha solo si la izquierda es null o undefined. Un 0, una cadena vacía o un false se respetan:
const ver = (v) => (typeof v === "string" ? `"${v}"` : String(v));
const valores = [undefined, null, 0, "", false, NaN, "hola"];
for (const x of valores) {
console.log(ver(x).padEnd(10), "||", ver(x || "def").padEnd(7), "??", ver(x ?? "def"));
}salidaundefined || "def" ?? "def" null || "def" ?? "def" 0 || "def" ?? 0 "" || "def" ?? "" false || "def" ?? false NaN || "def" ?? NaN "hola" || "hola" ?? "hola"
||, todo lo falso se sustituye. Con ??, solo null y undefined: el 0, la cadena vacía, false y NaN se quedan como están. Esquema: decodigo.com.La regla práctica: para valores por omisión, ??. Usa || solo cuando de verdad quieras tratar igual cualquier valor falso.
Una precaución: JavaScript no te deja mezclar ?? con || o && sin paréntesis, porque el orden sería ambiguo. Es un error al cargar el archivo, antes de ejecutar nada:
const a = null, b = 0; const r = a || b ?? "def";
salidaSyntaxError: Unexpected token '??'
Con paréntesis, (a || b) ?? "def", funciona y queda claro qué querías.
?. : el encadenamiento opcional
Este es el error más frecuente de JavaScript, el que verás cientos de veces: leer una propiedad de algo que es undefined.
const usuario = { nombre: "Ana" }; // no tiene dirección
console.log(usuario.direccion.ciudad);salidaTypeError: Cannot read properties of undefined (reading 'ciudad')
usuario.direccion no existe, así que vale undefined, y pedirle .ciudad a undefined rompe el programa. El mensaje dice exactamente eso: no se pueden leer propiedades de undefined, y entre paréntesis, cuál se intentaba leer. Con ?., si lo de la izquierda es null o undefined, la expresión entera se corta y da undefined, sin error:
const ana = { nombre: "Ana" };
const luis = { nombre: "Luis", direccion: { ciudad: "Lima" }, saludar() { return "hola"; } };
console.log(ana.direccion?.ciudad);
console.log(luis.direccion?.ciudad);
console.log(ana.direccion?.ciudad.toUpperCase()); // se corta antes de llegar aquí
console.log(ana.saludar?.(), "|", luis.saludar?.());
console.log(ana.pedidos?.[0]);
console.log(ana.direccion?.ciudad ?? "ciudad desconocida");salidaundefined Lima undefined undefined | hola undefined ciudad desconocida
Fíjate en la tercera línea: ?. corta toda la cadena, así que .toUpperCase() ni se intenta. Funciona también para llamar a un método que puede no existir, obj.metodo?.(), y para índices, lista?.[0]. Y combinado con ??, da un valor por omisión en una sola línea.
?. esconde el fallo: el programa sigue con un undefined que acabará dando un error mucho más lejos y más difícil de rastrear. Úsalo donde la ausencia sea normal, como un campo opcional de un formulario o de una respuesta JSON.Asignaciones lógicas: ||=, &&= y ??=
Son la versión corta de «asigna solo si…». La más útil es ??=, que rellena lo que falta sin pisar lo que ya hay:
const config = { puerto: 0, modo: "" };
config.puerto ??= 8080; // ya tiene un valor (0): no lo toca
config.host ??= "localhost";
config.modo ||= "producción";
console.log(config);salida{ puerto: 0, modo: 'producción', host: 'localhost' }El error frecuente: = dentro del if
Un solo signo igual asigna; tres comparan. Dentro de un if, la asignación es válida y devuelve el valor asignado, así que el programa no da ningún error: simplemente hace otra cosa.
let intentos = 3;
if (intentos = 0) {
console.log("sin intentos");
} else {
console.log("aún quedan... ¿o no?", intentos);
}salidaaún quedan... ¿o no? 0
La condición asignó 0 a intentos y, como 0 es falso, fue por el else, que encima muestra el valor ya destrozado. Compara siempre con ===; los editores con ESLint marcan este caso con la regla no-cond-assign. Hay más sorpresas de este tipo, como el return seguido de un salto de línea, en la página de rarezas de JavaScript.
Resumen
| Para | Usa | Cuidado con |
|---|---|---|
| Elegir entre dos valores | c ? a : b | anidar ternarios |
| Muchos casos de un mismo valor | switch | el break olvidado y que compara con === |
| Recorrer un array, una cadena, un Map | for...of | — |
| Recorrer las claves de un objeto | Object.entries() con for...of | for...in en arrays |
| Valor por omisión | ?? y ??= | || se come el 0 y la cadena vacía |
| Algo que puede faltar | ?. | usarlo donde algo debe existir |
Todo el código de esta lección se ejecutó con Node.js 24 en un contenedor limpio antes de publicarla; las salidas y los errores están copiados de esa ejecución.
