Hasta ahora tu código se ejecutaba en línea recta: de la primera línea a la última, sin desviarse nunca. Eso es suficiente para describir una tarea de Taller Nómada, pero no para razonar sobre ella. Marta no quiere que el programa le repita lo que ya sabe: quiere que le diga cuáles de sus tareas están en riesgo, si Iván puede marcar una tarea como hecha o si el formulario de alta tiene un dato inválido. Todo eso son decisiones, y una decisión en JavaScript se escribe con una sentencia condicional. En esta lección aprenderás a bifurcar el flujo con if, else y else if, a construir condiciones compuestas que se lean bien, a mantener el código plano en lugar de anidarlo hasta hacerlo ilegible, y a decidir cuándo un ternario mejora el código y cuándo lo empeora.
Contenido
- Del flujo recto a la bifurcación
- La sentencia
if else: el camino alternativoelse if: cadenas de decisión- Condiciones compuestas con
&&,||y! - Paréntesis: la diferencia entre correcto y legible
- Condiciones sobre valores truthy y falsy
- El problema del anidamiento
- Cláusulas de guarda y salida temprana
- El operador ternario y sus límites
- Caso práctico: clasificar la urgencia de una tarea
- Caso práctico: validar un cambio de estado (R6)
- Tabla de decisión de las reglas de negocio
- Errores Comunes y Consejos
- Ejercicios
- Conclusión
- Del flujo recto a la bifurcación
En Sintaxis y Conceptos Básicos viste que JavaScript ejecuta las sentencias de arriba abajo, una detrás de otra. Una estructura de control rompe esa linealidad: permite que ciertas sentencias se ejecuten solo en determinadas circunstancias, o que se ejecuten muchas veces.
Las estructuras de control se dividen en dos familias:
| Familia | Qué hace | Sentencias |
|---|---|---|
| Selección | Elige uno entre varios caminos | if, else, else if, switch |
| Iteración | Repite un bloque de sentencias | for, while, do...while |
Esta lección cubre la selección con if. El switch llega en Sentencias Switch y la iteración en Bucles: for, while, do-while.
- La sentencia
if
ifLa forma más simple: una condición y un bloque que se ejecuta solo si esa condición es verdadera.
const horasEstimadas = 46;
if (horasEstimadas > 40) {
console.warn('La tarea supera la semana laboral completa.');
}
console.log('Comprobación terminada.');Anatomía, pieza a pieza:
ifes la palabra reservada. No lleva paréntesis pegado obligatoriamente, pero se escribe siempreif (.(horasEstimadas > 40)es la condición. Los paréntesis son obligatorios. Dentro va cualquier expresión que produzca un valor; JavaScript lo convierte a booleano igual que hacíaBoolean()en Conversión de Tipos y Comparaciones.{ ... }es el bloque que se ejecuta si la condición es verdadera. Conletyconst, todo lo declarado dentro solo existe dentro.- No lleva
;después de la llave de cierre. Unifes una sentencia de bloque, no una expresión.
La salida del ejemplo:
Si cambias 46 por 12, el aviso no aparece y solo se imprime la segunda línea. El programa eligió no ejecutar un trozo de código.
2.1 Las llaves son opcionales, pero póntelas siempre
JavaScript permite omitir las llaves cuando el cuerpo tiene una sola sentencia:
Funciona, pero es una fuente clásica de errores. Mira qué ocurre al añadir una segunda línea:
// ✗ TRAMPA: la segunda línea se ejecuta SIEMPRE
if (horasEstimadas > 40)
console.warn('Supera las 40 horas.');
console.warn('Revísala con Marta.');La indentación sugiere que ambas líneas pertenecen al if, pero la indentación no significa nada en JavaScript. Sin llaves, el if solo gobierna la primera sentencia; la segunda es código normal que se ejecuta siempre. El error es invisible al leer y evidente al ejecutar.
Norma del curso: llaves siempre, aunque el cuerpo tenga una sola línea. No cuesta nada y elimina una categoría entera de fallos.
else: el camino alternativo
else: el camino alternativoelse define qué hacer cuando la condición es falsa. No lleva condición propia: es "en cualquier otro caso".
const estado = 'en-curso';
if (estado === 'hecha') {
console.log('Tarea completada. No requiere seguimiento.');
} else {
console.log('Tarea abierta. Sigue en el tablero.');
}Se ejecuta exactamente uno de los dos bloques, nunca los dos ni ninguno.
flowchart TD
A["estado === 'hecha'"] -->|true| B["Tarea completada"]
A -->|false| C["Tarea abierta"]
B --> D["Continúa el programa"]
C --> D
else if: cadenas de decisión
else if: cadenas de decisiónCuando hay más de dos caminos, se encadenan condiciones con else if. Clasificar la prioridad de una tarea en un peso numérico es el ejemplo canónico del proyecto:
const prioridad = 'media';
let peso;
if (prioridad === 'alta') {
peso = 3;
} else if (prioridad === 'media') {
peso = 2;
} else if (prioridad === 'baja') {
peso = 1;
} else {
peso = 0;
console.error(`Prioridad desconocida: "${prioridad}"`);
}
console.log(`Peso de la tarea: ${peso}`); // Peso de la tarea: 2Tres detalles importantes:
- Las condiciones se evalúan en orden y la cadena para en la primera verdadera. Si
prioridades'alta', las otras dos comparaciones ni siquiera se ejecutan. El orden importa. - El
elsefinal es la red de seguridad. Captura los valores que no esperabas: un'ALTA'en mayúsculas, unundefined, un error de escritura. Sin él,pesose quedaría enundefinedy el fallo aparecería mucho más adelante, en otro sitio, disfrazado deNaN. let peso;va antes delif. Si declararasconst peso = 3;dentro del bloque, esa variable solo existiría dentro de ese bloque y desaparecería al salir. Declarar fuera y asignar dentro es el patrón habitual.
Técnicamente else if no es una construcción nueva: es un else cuyo cuerpo es otro if. Se escribe en la misma línea por convención, para que la cadena se lea como una lista de casos y no como una escalera.
- Condiciones compuestas con
&&, || y !
&&, || y !En Operadores Básicos aprendiste los operadores lógicos y su cortocircuito. Dentro de un if es donde despliegan toda su utilidad.
| Operador | Se lee | Es verdadero cuando |
|---|---|---|
&& |
"y" | Ambos lados son verdaderos |
|| |
"o" | Al menos uno de los lados es verdadero |
! |
"no" | El valor de la derecha es falso |
La regla R10 del proyecto dice: una tarea vencida (fechaLimite pasada y estado distinto de 'hecha') se destaca. Son dos condiciones que deben cumplirse a la vez, así que se unen con &&:
const HOY = '2026-09-20';
const titulo = 'Presupuesto de la carpintería';
const fechaLimite = '2026-09-05';
const estado = 'pendiente';
if (fechaLimite < HOY && estado !== 'hecha') {
console.error(`VENCIDA: ${titulo} (venció el ${fechaLimite})`);
}Recuerda de El Proyecto del Curso por qué fechaLimite < HOY funciona sin conversiones: el formato ISO 'yyyy-mm-dd' ordena alfabéticamente igual que cronológicamente.
Con || expresamos "cualquiera de estas situaciones justifica revisar la tarea":
const responsable = null;
const horasEstimadas = 44;
if (responsable === null || horasEstimadas > 40) {
console.warn(`"${titulo}" necesita revisión de Marta.`);
}Y ! invierte una condición. Estas dos líneas son equivalentes:
La segunda es claramente mejor. Usa ! para negar variables booleanas, no para negar comparaciones: para eso existen !==, <= y >=.
const estaArchivada = false;
if (!estaArchivada) {
console.log('La tarea sigue visible en el tablero.');
}
- Paréntesis: la diferencia entre correcto y legible
&& tiene mayor precedencia que ||, igual que * la tiene sobre +. Eso significa que esta condición:
se agrupa así:
Puede que sea lo que querías, o puede que no. El problema es que quien lea el código no puede saberlo. Compara con la otra agrupación posible:
Con prioridad = 'alta' y horasEstimadas = 5, la primera versión da true y la segunda false. Son condiciones distintas que se parecen mucho.
Norma: en cuanto una condición mezcle && y ||, escribe los paréntesis aunque la precedencia ya haga lo correcto. No son para el motor: son para la persona que revise el código dentro de seis meses.
Cuando una condición crece demasiado, la mejor herramienta no son más paréntesis sino variables booleanas con nombre:
const HOY = '2026-09-20';
const prioridad = 'alta';
const estado = 'pendiente';
const fechaLimite = '2026-09-30';
const horasEstimadas = 12;
const estaVencida = fechaLimite < HOY && estado !== 'hecha';
const esImportante = prioridad === 'alta' || horasEstimadas > 20;
const estaAbierta = estado !== 'hecha';
if (estaAbierta && (estaVencida || esImportante)) {
console.log('Esta tarea entra en la revisión diaria de Marta.');
}El if final ahora se lee como una frase en español: está abierta, y o bien está vencida o bien es importante. Cada variable intermedia documenta una idea del negocio y puedes imprimirla por separado cuando algo no cuadre. Esta técnica —dar nombre a los trozos de una condición— es la que más mejora la legibilidad del código real.
- Condiciones sobre valores truthy y falsy
En Conversión de Tipos y Comparaciones viste los ocho valores falsy: false, 0, -0, 0n, '', null, undefined y NaN. Todo lo demás es truthy. Un if aplica exactamente esa conversión a su condición.
Esto permite escribir comprobaciones muy cortas:
const revisor = null;
if (revisor) {
console.log(`Revisa: ${revisor}`);
} else {
console.log('Tarea sin revisor asignado.');
}Cómodo, pero peligroso, porque if (valor) mete en el mismo saco situaciones muy distintas. Observa qué pasa con los campos del proyecto:
| Comprobación | 0 |
'' |
null |
undefined |
[] |
'0' |
|---|---|---|---|---|---|---|
if (valor) |
✗ no entra | ✗ no entra | ✗ no entra | ✗ no entra | ✓ entra | ✓ entra |
Dos consecuencias directas sobre Nómada Tareas:
horasEstimadas puede ser un número válido y falsy. Si un día se admitieran tareas de 0 horas, if (horasEstimadas) las trataría como si el campo faltase. La comprobación honesta es explícita:
const horasEstimadas = 0;
if (horasEstimadas > 0 && horasEstimadas <= 40) {
console.log('Horas válidas.');
} else {
console.error('R3 incumplida: las horas deben estar entre 0 (exclusive) y 40.');
}Un array vacío es truthy. if (etiquetas) da true aunque no haya ninguna etiqueta, porque el array existe. Como etiquetas es siempre un array (nunca null), lo que hay que mirar es su longitud:
const etiquetas = [];
if (etiquetas.length === 0) {
console.log('Tarea sin etiquetar.');
} else {
console.log(`Tiene ${etiquetas.length} etiqueta(s).`);
}Norma práctica: usa if (valor) solo cuando de verdad quieras decir "hay algo utilizable aquí" y ninguno de los valores falsy sea legítimo para ese campo. En todos los demás casos, escribe la comparación completa: === null, > 0, .length === 0, !== ''. El código gana en claridad lo que pierde en brevedad.
- El problema del anidamiento
Un if puede contener otro if. La primera vez que se necesita, el resultado parece razonable:
// ✗ Difícil de seguir
const estado = 'pendiente';
const responsable = 'Iván';
const fechaLimite = '2026-09-30';
const HOY = '2026-09-20';
if (estado !== 'hecha') {
if (responsable !== null) {
if (fechaLimite < HOY) {
console.error('Vencida y asignada.');
} else {
console.log('Asignada y en plazo.');
}
} else {
console.warn('Abierta pero sin responsable.');
}
} else {
console.log('Terminada.');
}Este código funciona, pero tiene tres problemas serios:
- El caso importante está enterrado. Para saber cuándo se imprime "Vencida y asignada" hay que reconstruir mentalmente tres condiciones acumuladas.
- Los
elseestán lejos de suif. Elelsede la línea final pertenece al primerif, veinte líneas más arriba. - Cada nivel nuevo multiplica los caminos. Tres niveles ya dan ocho combinaciones posibles.
A esta forma se la llama código en flecha (arrow code), porque la indentación dibuja una punta de flecha hacia la derecha. Es una de las señales más fiables de que una lógica necesita reescribirse.
- Cláusulas de guarda y salida temprana
La alternativa es la cláusula de guarda: en lugar de anidar el caso bueno dentro de comprobaciones sucesivas, se descartan primero los casos excepcionales y se deja el camino principal al final, sin indentar.
Reescribimos el ejemplo anterior como una cadena plana de else if, ordenada del caso más excluyente al más general:
const estado = 'pendiente';
const responsable = 'Iván';
const fechaLimite = '2026-09-30';
const HOY = '2026-09-20';
let mensaje;
if (estado === 'hecha') {
mensaje = 'Terminada.';
} else if (responsable === null) {
mensaje = 'Abierta pero sin responsable.';
} else if (fechaLimite < HOY) {
mensaje = 'Vencida y asignada.';
} else {
mensaje = 'Asignada y en plazo.';
}
console.log(mensaje);Mismo comportamiento, un solo nivel de indentación y cuatro casos que se leen como una lista. Cada línea responde a una pregunta y la cadena se detiene en la primera respuesta afirmativa, así que las condiciones posteriores pueden dar por supuesto todo lo anterior: cuando se evalúa fechaLimite < HOY, ya sabemos con certeza que la tarea no está hecha y que tiene responsable.
Un segundo patrón, muy útil en validaciones, es acumular el motivo del rechazo en una variable:
const titulo = ' ';
const horasEstimadas = 52;
let error = '';
if (titulo.trim() === '') {
error = 'R2: el título no puede estar vacío.';
} else if (horasEstimadas <= 0) {
error = 'R3: las horas deben ser mayores que 0.';
} else if (horasEstimadas > 40) {
error = 'R3: las horas no pueden superar 40.';
}
if (error !== '') {
console.error(`Tarea rechazada. ${error}`);
} else {
console.log('Tarea aceptada.');
}titulo.trim() elimina los espacios de los extremos, de modo que un título con solo espacios se detecta como vacío: exactamente lo que exige la regla R2.
Una nota sobre el nombre. "Salida temprana" describe salir inmediatamente de un bloque de lógica en cuanto se detecta un caso inválido. Su forma canónica usa return dentro de una función, y esa herramienta llega en Definición y Llamada de Funciones. Mientras tanto, la cadena plana de else if te da el mismo beneficio principal —cero anidamiento y casos ordenados— con lo que ya sabes. Cuando conozcas las funciones, este mismo código se convertirá en tres líneas con return y te resultará natural.
- El operador ternario y sus límites
El ternario condicion ? valorSiVerdadero : valorSiFalso que viste en Operadores Básicos es una expresión: produce un valor. Esa es la clave para saber cuándo usarlo.
Úsalo para asignar o interpolar un valor, donde brilla:
const responsable = null;
const estado = 'hecha';
const horasEstimadas = 12;
const quien = responsable === null ? 'sin asignar' : responsable;
const icono = estado === 'hecha' ? '✓' : '○';
console.log(`${icono} Rediseñar la sala polivalente · ${quien} · ${horasEstimadas} h`);
// ✓ Rediseñar la sala polivalente · sin asignar · 12 hCon un if/else harían falta cinco líneas y una variable let que después no vuelve a cambiar. El ternario permite mantener const, que es siempre preferible.
No lo uses para ejecutar acciones. Esto funciona pero es un abuso: el ternario está pensado para producir valores, no para elegir efectos secundarios.
// ✗ Abuso: se usa el valor de retorno para nada
estado === 'hecha' ? console.log('Hecha') : console.error('Pendiente');
// ✓ Un if/else expresa exactamente lo que ocurre
if (estado === 'hecha') {
console.log('Hecha');
} else {
console.error('Pendiente');
}No lo anides más de una vez. Un ternario dentro de otro todavía se lee si se formatea en columna, pero tres ya no:
// ✗ Ilegible
const peso = prioridad === 'alta' ? 3 : prioridad === 'media' ? 2 : prioridad === 'baja' ? 1 : 0;
// △ Aceptable si se alinea, pero un if/else if es más claro
const pesoAlineado =
prioridad === 'alta' ? 3 :
prioridad === 'media' ? 2 :
prioridad === 'baja' ? 1 :
0;Cuando la cadena de casos crece, la construcción adecuada es un if/else if o un switch, que verás en la lección 02-03.
| Situación | Herramienta recomendada |
|---|---|
| Asignar uno de dos valores | Ternario |
| Elegir un texto dentro de un template literal | Ternario |
| Ejecutar sentencias distintas según el caso | if / else |
| Tres o más casos sobre la misma variable | if / else if o switch |
Condición con && y || mezclados |
if con variables booleanas con nombre |
- Caso práctico: clasificar la urgencia de una tarea
Marta necesita una etiqueta de urgencia que combine dos señales: la prioridad y los días que faltan para la fecha límite. Este es el primer trozo de lógica de negocio real del proyecto.
const HOY = '2026-09-20';
const titulo = 'Rediseñar la sala polivalente';
const prioridad = 'alta';
const estado = 'en-curso';
const fechaLimite = '2026-09-30';
// Días restantes: convertimos las fechas ISO a milisegundos y restamos.
const MS_POR_DIA = 1000 * 60 * 60 * 24;
const diasRestantes = Math.round((new Date(fechaLimite) - new Date(HOY)) / MS_POR_DIA);
// Señales del negocio, cada una con su nombre
const estaCerrada = estado === 'hecha';
const estaVencida = diasRestantes < 0;
const esInminente = diasRestantes >= 0 && diasRestantes <= 3;
const esPrioritaria = prioridad === 'alta';
let urgencia;
if (estaCerrada) {
urgencia = 'ninguna';
} else if (estaVencida) {
urgencia = 'crítica';
} else if (esInminente && esPrioritaria) {
urgencia = 'crítica';
} else if (esInminente || esPrioritaria) {
urgencia = 'alta';
} else if (diasRestantes <= 14) {
urgencia = 'media';
} else {
urgencia = 'baja';
}
console.log(`${titulo}: urgencia ${urgencia} (faltan ${diasRestantes} días)`);
// Rediseñar la sala polivalente: urgencia alta (faltan 10 días)Qué hay que entender aquí:
new Date(fechaLimite) - new Date(HOY)aprovecha algo que ya conoces: cuando se usa el operador-, JavaScript convierte cada fecha a su número de milisegundos desde 1970. Restarlas da la distancia en milisegundos; dividir entreMS_POR_DIAla convierte en días. Es el único caso del proyecto donde usamosDate, tal como se anunció en la lección 01-08.- El orden de la cadena es lógica de negocio, no capricho. Una tarea hecha nunca es urgente aunque haya vencido, así que
estaCerradava primero. Una vencida es crítica sin más comprobaciones, así que va segunda. - Las cuatro variables booleanas hacen la cadena autoexplicativa. Sin ellas, la cuarta condición sería
(diasRestantes >= 0 && diasRestantes <= 3) || prioridad === 'alta', correcta pero opaca.
- Caso práctico: validar un cambio de estado (R6)
La regla R6 dice que solo son válidas las transiciones del ciclo de vida. Recuerda el diagrama de la lección 01-08:
stateDiagram-v2
[*] --> pendiente: se crea
pendiente --> en_curso: alguien empieza
en_curso --> hecha: se termina
en_curso --> pendiente: se aparca
hecha --> en_curso: hay que retocarla
Traducido a condiciones: son válidas cuatro transiciones y ninguna más. Iván intenta pasar una tarea de 'pendiente' directamente a 'hecha', y el programa debe rechazarlo con un motivo comprensible.
const estadoActual = 'pendiente';
const estadoNuevo = 'hecha';
const ES_ESTADO_VALIDO =
estadoNuevo === 'pendiente' || estadoNuevo === 'en-curso' || estadoNuevo === 'hecha';
let permitido;
let motivo;
if (!ES_ESTADO_VALIDO) {
permitido = false;
motivo = `"${estadoNuevo}" no es un estado del sistema.`;
} else if (estadoActual === estadoNuevo) {
permitido = false;
motivo = `La tarea ya está en estado "${estadoActual}".`;
} else if (estadoActual === 'pendiente' && estadoNuevo === 'en-curso') {
permitido = true;
motivo = 'Alguien empieza la tarea.';
} else if (estadoActual === 'en-curso' && estadoNuevo === 'hecha') {
permitido = true;
motivo = 'Tarea terminada.';
} else if (estadoActual === 'en-curso' && estadoNuevo === 'pendiente') {
permitido = true;
motivo = 'La tarea se aparca.';
} else if (estadoActual === 'hecha' && estadoNuevo === 'en-curso') {
permitido = true;
motivo = 'Se reabre para retocarla.';
} else {
permitido = false;
motivo = `Transición no permitida: ${estadoActual} → ${estadoNuevo}.`;
}
if (permitido) {
console.log(`✓ ${motivo}`);
} else {
console.error(`✗ ${motivo}`);
}
// ✗ Transición no permitida: pendiente → hecha.Fíjate en la estructura: dos guardas al principio (estado inexistente, estado repetido), cuatro casos permitidos en medio y un else que rechaza todo lo demás. Rechazar por defecto es la política correcta en una validación: si mañana alguien añade un estado nuevo y olvida su transición, el sistema la rechazará en lugar de aceptarla en silencio.
Esta cadena de seis else if es larga, y hay una razón: cada rama compara dos variables. En la lección 02-03 verás que un switch no mejora este caso concreto, y en el Módulo 4 lo resolverás con un objeto de consulta en cuatro líneas. De momento, esta versión es explícita y correcta, que es lo que importa.
- Tabla de decisión de las reglas de negocio
Antes de escribir un if complicado conviene dibujar una tabla de decisión: una fila por caso, una columna por condición y una columna con el resultado. Esta es la de las validaciones que Nómada Tareas aplica al aceptar una tarea nueva.
| # | Condición | Regla | Resultado si se incumple |
|---|---|---|---|
| 1 | titulo.trim() !== '' |
R2 | Rechazar: título vacío |
| 2 | titulo.length <= 100 |
R2 | Rechazar: título demasiado largo |
| 3 | horasEstimadas > 0 |
R3 | Rechazar: horas no positivas |
| 4 | horasEstimadas <= 40 |
R3 | Rechazar: supera la semana laboral |
| 5 | fechaLimite >= HOY |
R4 | Rechazar: fecha límite en el pasado |
| 6 | prioridad ∈ {alta, media, baja} |
— | Rechazar: prioridad desconocida |
| 7 | estado === 'pendiente' |
R5 | Rechazar: una tarea nueva nace pendiente |
| 8 | responsable es null o del equipo |
R8 | Rechazar: responsable no reconocido |
La tabla se traduce casi mecánicamente a una cadena de guardas: cada fila es un else if que asigna el mensaje de rechazo. Escribirla antes de programar tiene dos ventajas: descubres casos olvidados mientras la rellenas, y te da la lista de pruebas que ejecutarás en el Módulo 8.
Errores Comunes y Consejos
Usar = en lugar de == o ===. El error clásico.
= asigna, no compara. La expresión vale 'hecha', que es truthy, así que la condición siempre se cumple y además has destruido el valor original. Con const obtendrás un TypeError que te salvará; con let, un fallo silencioso. Lee siempre === en voz alta como "es igual a" y = como "recibe".
Comparar con == por costumbre. '0' == false es true y null == 0 es false. Usa === siempre, tal como quedó establecido en la lección 01-07.
Poner ; después de la condición.
El ; es el cuerpo vacío del if, y el bloque siguiente es un bloque suelto que se ejecuta pase lo que pase. No hay error ni aviso.
Encadenar comparaciones como en matemáticas. if (0 < horas < 40) no hace lo que parece: evalúa 0 < horas, obtiene true o false, y lo compara con 40 convirtiéndolo a 1 o 0. Siempre da true. Lo correcto es horas > 0 && horas < 40.
Olvidar el else final. Si una cadena else if asigna una variable, sin else esa variable puede quedarse en undefined. Añade siempre un caso por defecto, aunque solo sea para registrar un error.
Repetir la variable en cada comparación. prioridad === 'alta' || 'media' no compara con 'media': evalúa prioridad === 'alta' y, si es falso, devuelve 'media', que es truthy. La condición se cumple siempre. Hay que escribir prioridad === 'alta' || prioridad === 'media'.
Consejo: convierte condiciones largas en booleanos con nombre. Es la técnica de esta lección que más impacto tiene en código real.
Consejo: ordena las cadenas del caso más específico al más general. Si el caso general va primero, los específicos nunca se alcanzan.
Ejercicios
Ejercicio 1 — Etiqueta de carga de trabajo
Marta quiere saber de un vistazo si una tarea es asumible. Escribe una cadena if/else if que, a partir de horasEstimadas, asigne a una variable carga uno de estos valores, y que rechace con un mensaje de error los valores que incumplen la regla R3:
| Horas | carga |
|---|---|
| Menor o igual que 0, o mayor que 40 | 'inválida' |
| De 1 a 4 | 'ligera' |
| De 5 a 15 | 'media' |
| De 16 a 40 | 'pesada' |
Pruébalo con 2, 12, 30 y -3.
Ejercicio 2 — Semáforo de la tarea
Con estas variables, calcula una variable semaforo con valor '🔴', '🟠' o '🟢' según estas reglas, usando variables booleanas con nombre para que el if final se lea como una frase:
- 🔴 si la tarea no está hecha y su fecha límite ya pasó.
- 🟠 si no está hecha, la fecha no ha pasado y la prioridad es
'alta'. - 🟢 en el resto de casos.
const HOY = '2026-09-20';
const titulo = 'Actualizar la web de reservas';
const estado = 'pendiente';
const prioridad = 'alta';
const fechaLimite = '2026-10-02';Imprime después una línea con el semáforo y el título. Resuelve la parte final (elegir el texto que acompaña al semáforo: 'urgente', 'atención' o 'en plazo') con un ternario donde sea razonable.
Ejercicio 3 — Quién puede cambiar el estado
Amplía la validación de la sección 12 con una regla adicional de Taller Nómada: solo el responsable de la tarea o Marta (coordinadora) pueden cambiarle el estado. Añade las variables responsable y usuario, y haz que la validación rechace el cambio con un motivo claro cuando el usuario no tenga permiso. Ten cuidado con el orden: comprobar el permiso debe ir antes de comprobar la transición, porque no tiene sentido explicar que la transición es inválida a alguien que no podía tocarla.
Pruébalo con: responsable 'Iván' y usuario 'Lucía' (debe rechazar por permiso), y responsable 'Iván' y usuario 'Marta' con transición en-curso → hecha (debe permitir).
Soluciones
Ejercicio 1
const horasEstimadas = 12;
let carga;
if (horasEstimadas <= 0 || horasEstimadas > 40) {
carga = 'inválida';
console.error(`R3 incumplida: ${horasEstimadas} h no es un valor admisible.`);
} else if (horasEstimadas <= 4) {
carga = 'ligera';
} else if (horasEstimadas <= 15) {
carga = 'media';
} else {
carga = 'pesada';
}
console.log(`Carga: ${carga}`); // Carga: mediaLa clave está en descartar primero los valores inválidos. Una vez superada esa guarda, sabemos que horasEstimadas está entre 0 (exclusive) y 40, así que las condiciones siguientes solo necesitan el límite superior: <= 4, <= 15 y el else. No hace falta escribir horasEstimadas >= 5 && horasEstimadas <= 15, porque llegar a esa rama ya garantiza que es mayor que 4. Esa acumulación implícita es la gran ventaja de las cadenas else if frente a if sueltos.
Con -3: entra en la primera rama, carga vale 'inválida' y se registra el error. Con 2: 'ligera'. Con 30: 'pesada'.
Ejercicio 2
const HOY = '2026-09-20';
const titulo = 'Actualizar la web de reservas';
const estado = 'pendiente';
const prioridad = 'alta';
const fechaLimite = '2026-10-02';
const estaAbierta = estado !== 'hecha';
const haVencido = fechaLimite < HOY;
const esPrioritaria = prioridad === 'alta';
let semaforo;
if (estaAbierta && haVencido) {
semaforo = '🔴';
} else if (estaAbierta && esPrioritaria) {
semaforo = '🟠';
} else {
semaforo = '🟢';
}
const nota = semaforo === '🔴' ? 'urgente' : semaforo === '🟠' ? 'atención' : 'en plazo';
console.log(`${semaforo} ${titulo} — ${nota}`);
// 🟠 Actualizar la web de reservas — atenciónLas tres variables booleanas convierten el if en una lectura directa: "si está abierta y ha vencido...". Sin ellas, la primera condición sería estado !== 'hecha' && fechaLimite < HOY, que exige releer dos veces.
El ternario doble para nota está en el límite de lo aceptable: son tres casos sobre una sola variable y se alinean visualmente. Si hubiera un cuarto semáforo, la respuesta correcta sería un if/else if o el switch de la próxima lección.
Ejercicio 3
const estadoActual = 'en-curso';
const estadoNuevo = 'hecha';
const responsable = 'Iván';
const usuario = 'Marta';
const ES_ESTADO_VALIDO =
estadoNuevo === 'pendiente' || estadoNuevo === 'en-curso' || estadoNuevo === 'hecha';
const tienePermiso = usuario === responsable || usuario === 'Marta';
let permitido;
let motivo;
if (!tienePermiso) {
permitido = false;
motivo = `${usuario} no puede modificar una tarea de ${responsable}.`;
} else if (!ES_ESTADO_VALIDO) {
permitido = false;
motivo = `"${estadoNuevo}" no es un estado del sistema.`;
} else if (estadoActual === estadoNuevo) {
permitido = false;
motivo = `La tarea ya está en estado "${estadoActual}".`;
} else if (estadoActual === 'pendiente' && estadoNuevo === 'en-curso') {
permitido = true;
motivo = 'Alguien empieza la tarea.';
} else if (estadoActual === 'en-curso' && estadoNuevo === 'hecha') {
permitido = true;
motivo = 'Tarea terminada.';
} else if (estadoActual === 'en-curso' && estadoNuevo === 'pendiente') {
permitido = true;
motivo = 'La tarea se aparca.';
} else if (estadoActual === 'hecha' && estadoNuevo === 'en-curso') {
permitido = true;
motivo = 'Se reabre para retocarla.';
} else {
permitido = false;
motivo = `Transición no permitida: ${estadoActual} → ${estadoNuevo}.`;
}
console.log(permitido ? `✓ ${motivo}` : `✗ ${motivo}`);
// ✓ Tarea terminada.Con usuario = 'Lucía' y responsable = 'Iván', la primera guarda corta inmediatamente: ✗ Lucía no puede modificar una tarea de Iván. Y observa que ni siquiera se evalúa si la transición era válida, que es justo lo que pedía el enunciado.
tienePermiso merece su propia variable porque expresa un concepto del negocio —autorización— distinto del concepto de transición válida. Cuando mañana el permiso se complique (por ejemplo, permitir también a un revisor asignado), solo cambia esa línea.
El ternario del console.log está bien empleado: elige un valor —el prefijo ✓ o ✗— dentro de una plantilla, que es exactamente su cometido.
Conclusión
Tu código ya toma decisiones. Sabes escribir un if con llaves siempre, añadir un else para el camino alternativo y encadenar else if para clasificar en más de dos casos, aprovechando que cada rama da por supuesto todo lo que descartaron las anteriores. Sabes combinar condiciones con &&, || y !, poner paréntesis por legibilidad aunque la precedencia ya sea correcta, y —lo más importante— dar nombre a los trozos de una condición para que el if final se lea como una frase del negocio.
También sabes evitar dos trampas frecuentes: confiar en truthy/falsy cuando 0, '' o un array vacío son valores legítimos, y anidar if dentro de if hasta dibujar una flecha en el margen. La cadena plana de guardas es tu herramienta contra el anidamiento, y el ternario tu herramienta para asignar uno de dos valores, nunca para ejecutar acciones ni para encadenar cuatro casos.
Aplicado al proyecto, ya has implementado dos piezas reales de Nómada Tareas: la clasificación de urgencia que combina prioridad y días restantes, y la validación de transiciones de estado de la regla R6, con permisos incluidos.
Pero fíjate en algo: toda esa lógica se aplica a una sola tarea. Marta tiene un backlog de seis, y el mes que viene tendrá cuarenta. Copiar el bloque de urgencia cuarenta veces no es una opción. Eso se resuelve en Bucles: for, while, do-while, donde aprenderás a repetir un bloque de código tantas veces como haga falta y a recorrer todas las tareas del taller para sumar sus horas, encontrar la más urgente y contar cuántas lleva cada persona. Con condicionales dentro de bucles tendrás, por fin, un programa que razona sobre datos de verdad.
Curso de JavaScript: De Principiante a Avanzado
Módulo 1: Introducción a JavaScript
- ¿Qué es JavaScript?
- Configuración de tu Entorno de Desarrollo
- Tu Primer Programa en JavaScript
- Sintaxis y Conceptos Básicos de JavaScript
- Variables y Tipos de Datos
- Operadores Básicos
- Conversión de Tipos y Comparaciones
- El Proyecto del Curso: Nómada Tareas
Módulo 2: Estructuras de Control
- Sentencias Condicionales
- Bucles: for, while, do-while
- Sentencias Switch
- Control del Flujo: break, continue y Bucles Anidados
- Manejo de Errores con try-catch
Módulo 3: Funciones
- Definición y Llamada de Funciones
- Expresiones de Función y Funciones Flecha
- Parámetros y Valores de Retorno
- Ámbito y Closures
- Hoisting y el Contexto de Ejecución
- Funciones de Orden Superior
- Recursividad
Módulo 4: Objetos y Arrays
- Introducción a los Objetos
- Métodos de Objeto y la Palabra Clave
this - Arrays: Conceptos Básicos y Métodos
- Iteración sobre Arrays
- Buscar, Ordenar y Agregar Datos: find, sort y reduce
- Desestructuración de Arrays
- Desestructuración de Objetos, Spread y Rest
- JSON y Copias de Objetos
Módulo 5: Objetos y Funciones Avanzadas
- Prototipos y Herencia
- Clases y Programación Orientada a Objetos
- Encapsulación: Getters, Setters y Campos Privados
- Módulos e Importación/Exportación
- JavaScript Asíncrono: Callbacks
- Promesas y Async/Await
- El Bucle de Eventos y la Cola de Microtareas
- Iteradores y Generadores
Módulo 6: El Modelo de Objetos del Documento (DOM)
- Introducción al DOM
- Selección y Manipulación de Elementos del DOM
- Manejo de Eventos
- Propagación, Delegación y Eventos Personalizados
- Creación y Eliminación de Elementos del DOM
- Renderizado de Listas y Plantillas HTML
- Manejo y Validación de Formularios
Módulo 7: APIs del Navegador y Temas Avanzados
- Almacenamiento Local y de Sesión
- Fetch API y AJAX
- Peticiones Robustas: Errores, Timeouts y AbortController
- WebSockets
- Service Workers y Aplicaciones Web Progresivas (PWAs)
- APIs del Navegador Esenciales
- Introducción a WebAssembly
Módulo 8: Pruebas y Depuración
- Depuración de JavaScript
- Calidad de Código: ESLint, Prettier y Convenciones
- Pruebas Unitarias con Jest
- Dobles de Prueba: Mocks, Stubs y Spies
- Pruebas de Integración
- Pruebas de Extremo a Extremo con Cypress
Módulo 9: Rendimiento y Optimización
- Medir Antes de Optimizar: DevTools y Web Vitals
- Optimización del Rendimiento de JavaScript
- Gestión de Memoria
- Manipulación Eficiente del DOM
- Carga Perezosa y División de Código
Módulo 10: Frameworks y Librerías de JavaScript
- Por Qué Existen los Frameworks
- Introducción a React
- Gestión de Estado con Redux
- Conceptos Básicos de Vue.js
- Conceptos Básicos de Angular
- Elegir el Framework Adecuado
