Cerraste el Módulo 2 con una queja legítima: la cadena que convierte prioridad en un peso la habías escrito cuatro veces, la validación de transiciones aparecía duplicada y el bloque de cuarenta líneas que valida una tarea nueva no servía para validar una tarea editada sin copiarlo entero. Esta lección resuelve exactamente eso. Vas a aprender a empaquetar un trozo de lógica bajo un nombre, guardarlo y ejecutarlo tantas veces como quieras con datos distintos cada vez. Es el concepto más importante del curso hasta ahora: a partir de aquí, Nómada Tareas deja de ser un guion lineal para convertirse en un conjunto de piezas con nombre propio que puedes combinar, leer y —en el Módulo 8— probar por separado.
Contenido
- El problema real: el código que se repite
- Tu primera función: la declaración
- Qué ocurre exactamente en cada llamada
- El vocabulario preciso, en una tabla
- Parámetros: la misma lógica con datos distintos
return: salir con un valor- Funciones sin parámetros y funciones sin retorno
- Cómo nombrar bien una función
- La regla de la responsabilidad única
- La pila de llamadas, a nivel intuitivo
- Caso práctico: refactorizar el Módulo 2
- Errores Comunes y Consejos
- Ejercicios
- Conclusión
- El problema real: el código que se repite
Recupera un fragmento del Módulo 2. Este bloque convierte una prioridad en un peso numérico para calcular el esfuerzo ponderado del backlog:
let peso;
if (prioridad === 'alta') {
peso = 3;
} else if (prioridad === 'media') {
peso = 2;
} else if (prioridad === 'baja') {
peso = 1;
} else {
peso = 0;
}Nueve líneas. El problema no es la longitud: es que ese bloque exacto aparecía en cuatro sitios distintos del código del módulo anterior. Y eso tiene tres consecuencias muy concretas:
- El cambio se multiplica. Si mañana Marta decide que la prioridad alta pesa
5en lugar de3, hay que tocar cuatro sitios. Tocarás tres y el cuarto quedará mal. - El error se multiplica. Si en una de las copias escribiste
'medía'con tilde, tienes un fallo que solo se manifiesta en una parte del programa. - La intención se pierde. Al leer el fichero ves nueve líneas de
if, no ves "aquí se calcula el peso de una prioridad". El qué queda enterrado bajo el cómo.
Una función resuelve las tres cosas de golpe:
function pesoDePrioridad(prioridad) {
if (prioridad === 'alta') return 3;
if (prioridad === 'media') return 2;
if (prioridad === 'baja') return 1;
return 0;
}A partir de ahora, en los cuatro sitios escribes pesoDePrioridad(prioridad). La lógica vive en un solo lugar, se cambia en un solo lugar y el nombre dice qué hace sin que haya que leer el interior.
Una función es a la lógica lo que una variable es a un valor: darle un nombre a algo para poder referirte a ello sin repetirlo.
- Tu primera función: la declaración
La forma más básica de crear una función se llama declaración de función (function declaration):
Pieza a pieza:
| Parte | En el ejemplo | Qué es |
|---|---|---|
| Palabra clave | function |
Le dice al motor: "lo que viene es una función" |
| Nombre | saludarAlEquipo |
El identificador con el que la llamarás |
| Paréntesis | () |
Aquí van los parámetros (aquí, ninguno) |
| Llaves | { ... } |
Delimitan el cuerpo: el código que se ejecutará |
Fíjate en un detalle importante: escribir esa declaración no imprime nada. Definir una función es como escribir una receta en un papel; no se cocina nada hasta que alguien la sigue. Para ejecutarla hay que llamarla, y llamarla consiste en escribir su nombre seguido de paréntesis:
function saludarAlEquipo() {
console.log('Bienvenida al panel de Nómada Tareas.');
}
saludarAlEquipo(); // Bienvenida al panel de Nómada Tareas.
saludarAlEquipo(); // Bienvenida al panel de Nómada Tareas.Los paréntesis son obligatorios y son lo que distingue usar la función de nombrarla:
saludarAlEquipo; // No hace nada: es solo una referencia a la función
saludarAlEquipo(); // Sí la ejecutaEste es uno de los despistes más frecuentes al empezar. Si llamas a una función y "no pasa nada", lo primero que hay que mirar son los paréntesis.
Nota sobre el orden: las declaraciones de función se pueden llamar antes de la línea donde están escritas. El motor las prepara antes de ejecutar el fichero. Esto se llama hoisting y lo estudiarás con detalle en Hoisting y el Contexto de Ejecución; por ahora, escribe las funciones arriba y las llamadas abajo, que es lo más legible.
- Qué ocurre exactamente en cada llamada
Cuando escribes pesoDePrioridad('alta'), el motor hace esta secuencia:
sequenceDiagram
participant P as Código principal
participant F as pesoDePrioridad
P->>P: Evalúa el argumento 'alta'
P->>F: Salta al cuerpo con prioridad = 'alta'
Note over F: Ejecuta el cuerpo<br/>línea a línea
F-->>P: return 3
Note over P: La llamada se sustituye<br/>por el valor 3
P->>P: Continúa en la línea siguiente
Cuatro ideas que conviene tener muy claras desde el principio:
- Los argumentos se evalúan antes de entrar. En
pesoDePrioridad(prioridades[i]), primero se resuelveprioridades[i](por ejemplo,'alta') y ese valor ya resuelto es el que entra. - La ejecución del código que llama se detiene mientras la función trabaja, y se reanuda justo donde estaba cuando la función termina.
- Cada llamada es independiente. Las variables declaradas dentro del cuerpo nacen al entrar y desaparecen al salir. Dos llamadas no comparten nada. Esto lo formalizarás en Ámbito y Closures.
- Una llamada es una expresión: se puede usar en cualquier sitio donde encaje un valor.
const peso = pesoDePrioridad('media'); // en una asignación
console.log(`Peso: ${pesoDePrioridad('alta')}`); // dentro de un template literal
if (pesoDePrioridad(prioridades[0]) >= 3) { /* ... */ } // dentro de una condición
const esfuerzo = pesoDePrioridad('alta') * 12; // dentro de una operaciónEsa última línea vale la pena leerla despacio: la llamada devuelve 3, la expresión se convierte en 3 * 12 y esfuerzo acaba valiendo 36. La función se sustituye por lo que devuelve.
- El vocabulario preciso, en una tabla
Al hablar de funciones se usan seis términos que conviene no confundir, porque los mensajes de error y la documentación los usan con precisión:
| Término | Qué es | En el ejemplo |
|---|---|---|
| Declaración | El código que crea la función | function pesoDePrioridad(prioridad) { ... } |
| Nombre | El identificador de la función | pesoDePrioridad |
| Parámetro | La variable declarada entre paréntesis en la definición | prioridad |
| Argumento | El valor real que pasas al llamar | 'alta' en pesoDePrioridad('alta') |
| Cuerpo | El bloque entre llaves que se ejecuta | Los cuatro if/return |
| Llamada / invocación | El acto de ejecutarla | pesoDePrioridad('alta') |
| Valor de retorno | Lo que la llamada produce | 3 |
La distinción parámetro / argumento es la que más se confunde y la más útil. Una regla mnemotécnica: el parámetro está en la plantilla (la definición); el argumento es el valor actual de una llamada concreta.
// ↓ parámetro (existe una vez, en la definición)
function esTareaVencida(fechaLimite, estado, hoy) {
return fechaLimite < hoy && estado !== 'hecha';
}
// ↓ argumentos (distintos en cada llamada)
esTareaVencida('2026-09-05', 'pendiente', '2026-09-20'); // true
esTareaVencida('2026-11-05', 'en-curso', '2026-09-20'); // false
- Parámetros: la misma lógica con datos distintos
Los parámetros son lo que convierte una función en una herramienta reutilizable en lugar de en un simple atajo. Se declaran separados por comas y se comportan como variables locales que ya vienen con valor:
// Devuelve el título recortado a un número máximo de caracteres.
function acortarTitulo(titulo, maximo) {
if (titulo.length <= maximo) {
return titulo;
}
return titulo.slice(0, maximo - 1) + '…';
}
console.log(acortarTitulo('Rediseñar la sala polivalente', 20));
// Rediseñar la sala p…
console.log(acortarTitulo('Presupuesto de la carpintería', 40));
// Presupuesto de la carpinteríaDos detalles clave del ejemplo:
- El orden importa.
acortarTitulo(20, 'Rediseñar…')no falla con un mensaje claro: intenta leer.lengthde un número, obtieneundefined, comparaundefined <= 'Rediseñar…'y devuelve algo absurdo. Los argumentos se emparejan por posición, no por nombre. tituloymaximosolo existen dentro del cuerpo. Fuera de la función no están declaradas; si intentas usarlas, obtendrás unReferenceError.
return: salir con un valor
return: salir con un valorreturn hace dos cosas a la vez, y ambas son importantes:
- Termina la función inmediatamente. Ninguna línea posterior del cuerpo se ejecuta.
- Entrega un valor al punto desde donde se llamó.
function clasificarCarga(horas) {
if (horas > 40) return 'sobrecarga';
if (horas > 30) return 'alta';
if (horas > 15) return 'normal';
return 'holgada';
console.log('Esto NUNCA se imprime'); // código muerto
}
console.log(clasificarCarga(25)); // normal
console.log(clasificarCarga(48)); // sobrecargaEste estilo —una cadena de return en lugar de un if/else if/else con una variable temporal— es la versión de función de las cláusulas de guarda que aprendiste en Sentencias Condicionales. Cada línea se lee como una regla independiente, sin anidamiento.
En Parámetros y Valores de Retorno profundizarás en qué devolver, cómo devolver varios datos a la vez y por qué conviene devolver siempre el mismo tipo. Por ahora basta con la mecánica.
- Funciones sin parámetros y funciones sin retorno
No todas las funciones reciben datos ni todas devuelven algo. Las cuatro combinaciones son legítimas y se usan para cosas distintas:
| Recibe | Devuelve | Para qué sirve | Ejemplo |
|---|---|---|---|
| Sí | Sí | Calcular o transformar | pesoDePrioridad('alta') |
| Sí | No | Provocar un efecto | imprimirTarea(tarea) |
| No | Sí | Producir algo nuevo cada vez | fechaDeHoy() |
| No | No | Ejecutar una rutina fija | mostrarCabecera() |
// Sin parámetros, con retorno: siempre produce el mismo formato
function fechaDeHoy() {
return '2026-09-20'; // en el proyecto usamos una fecha fija para poder razonar
}
// Con parámetros, sin retorno: su valor está en el efecto que produce
function imprimirSeparador(titulo) {
console.log('');
console.log(`── ${titulo} ${'─'.repeat(40 - titulo.length)}`);
}
imprimirSeparador('Backlog de Taller Nómada');Una función sin return no devuelve "nada": devuelve undefined. Esto es fuente de confusión, así que compruébalo:
const resultado = imprimirSeparador('Prueba');
console.log(resultado); // undefined
console.log(typeof resultado); // undefinedY cuidado con la trampa clásica: una función que hace console.log no devuelve lo que imprime.
function malCalculo(horas) {
console.log(horas * 2); // imprime, pero no devuelve
}
const doble = malCalculo(12); // imprime 24
console.log(doble + 1); // NaN ← undefined + 1Regla práctica: console.log es para mirar; return es para usar. Si el valor lo va a consumir otra parte del programa, tiene que salir por return.
- Cómo nombrar bien una función
El nombre de una función es documentación gratuita. La convención universal en JavaScript es verbo + sustantivo, en camelCase:
| Tipo de función | Patrón de nombre | Ejemplos del proyecto |
|---|---|---|
| Calcula y devuelve un valor | calcular…, obtener…, contar… |
calcularUrgencia, obtenerCargaDe, contarPendientes |
| Devuelve un booleano | es…, esta…, tiene…, puede… |
esTareaVencida, estaCompleta, tieneEtiqueta, puedeCambiarEstado |
| Transforma un dato en otro formato | formatear…, describir…, a… |
formatearFecha, describirTarea, aMinusculas |
| Provoca un efecto | imprimir…, guardar…, registrar… |
imprimirBacklog, guardarTarea, registrarCambio |
| Comprueba y lanza error si falla | validar…, asegurar… |
validarTarea, asegurarHorasValidas |
Y los nombres que hay que evitar, con el porqué:
| Nombre malo | Problema |
|---|---|
hacerCosa(), procesar() |
No dice qué hace; cualquier código cabe ahí dentro |
datos(), tarea() |
Un sustantivo suelto parece una variable, no una acción |
check() |
Ambiguo: ¿devuelve un booleano o lanza un error? |
calcularUrgenciaYGuardar() |
La conjunción "y" avisa de que hace dos cosas (ver apartado 9) |
fn2(), aux() |
Nombres sin significado; en tres semanas nadie sabrá qué son |
Un matiz que ahorra muchos errores: las funciones que devuelven un booleano se nombran como una pregunta afirmativa, para que al usarlas en un if se lean como una frase.
if (esTareaVencida('2026-09-05', 'pendiente', HOY)) {
console.warn('R10: tarea vencida.');
}
// se lee: "si es tarea vencida, avisa"
- La regla de la responsabilidad única
Una función debe hacer una sola cosa y debe poder explicarse en una frase sin la palabra "y". Este es un ejemplo real de lo que ocurre cuando se ignora la regla:
// ✗ Hace cuatro cosas: calcular, formatear, imprimir y avisar
function procesarTarea(titulo, prioridad, fechaLimite, estado, horas, hoy) {
let peso;
if (prioridad === 'alta') peso = 3;
else if (prioridad === 'media') peso = 2;
else if (prioridad === 'baja') peso = 1;
else peso = 0;
const esfuerzo = peso * horas;
const vencida = fechaLimite < hoy && estado !== 'hecha';
const marca = estado === 'hecha' ? '✓' : '○';
console.log(`${marca} ${titulo} · esfuerzo ${esfuerzo}`);
if (vencida) console.warn(`⚠ ${titulo} está vencida`);
}Esa función no se puede reutilizar para nada más: si quieres solo el esfuerzo, sin imprimir, no puedes. Si quieres saber si está vencida para pintarla en rojo en el Módulo 6, tampoco. Está todo soldado.
Ahora la misma lógica repartida en piezas:
flowchart TD
A["procesarTarea (todo junto)"] --> B["pesoDePrioridad<br/>prioridad → número"]
A --> C["estaVencida<br/>fecha + estado → booleano"]
A --> D["describirTarea<br/>datos → texto"]
A --> E["imprimirTarea<br/>texto → consola"]
B --> F["Reutilizable en el<br/>cálculo de esfuerzo"]
C --> G["Reutilizable en los<br/>avisos y en el filtro"]
D --> H["Reutilizable en la<br/>consola y en el DOM"]
Tres preguntas para saber si una función tiene demasiadas responsabilidades:
- ¿Su nombre necesita una "y"? → divídela.
- ¿Tiene más de 20-25 líneas de cuerpo? → probablemente hay dos ideas dentro.
- ¿Calcula algo y además lo imprime o lo guarda? → separa el cálculo del efecto.
- La pila de llamadas, a nivel intuitivo
Cuando una función llama a otra, el motor tiene que recordar dónde volver. Lo hace con una estructura llamada pila de llamadas (call stack): una pila de platos donde cada llamada añade un plato encima y cada return retira el de arriba.
function pesoDePrioridad(prioridad) {
if (prioridad === 'alta') return 3;
if (prioridad === 'media') return 2;
if (prioridad === 'baja') return 1;
return 0;
}
function esfuerzoDeTarea(prioridad, horas) {
return pesoDePrioridad(prioridad) * horas;
}
function informeDeTarea(titulo, prioridad, horas) {
const esfuerzo = esfuerzoDeTarea(prioridad, horas);
return `${titulo}: ${esfuerzo} puntos`;
}
console.log(informeDeTarea('Rediseñar la sala polivalente', 'alta', 12));
// Rediseñar la sala polivalente: 36 puntosAsí crece y se vacía la pila durante esa única línea:
flowchart LR
subgraph P1["1 · Llamada inicial"]
A1["informeDeTarea"]
A0["(global)"]
end
subgraph P2["2 · Entra esfuerzoDeTarea"]
B2["esfuerzoDeTarea"]
B1["informeDeTarea"]
B0["(global)"]
end
subgraph P3["3 · Entra pesoDePrioridad"]
C3["pesoDePrioridad"]
C2["esfuerzoDeTarea"]
C1["informeDeTarea"]
C0["(global)"]
end
subgraph P4["4 · Todo ha vuelto"]
D0["(global)"]
end
P1 --> P2 --> P3 --> P4
Lo importante hoy es la intuición: lo último que entra es lo primero que sale, y el programa siempre vuelve exactamente al punto donde se quedó. Esa pila es también la que aparece en los mensajes de error del Módulo 2 (at pesoDePrioridad, at esfuerzoDeTarea, …): el stack de un Error es literalmente una foto de esta pila. En Hoisting y el Contexto de Ejecución verás el mecanismo por dentro y qué ocurre cuando la pila se llena.
- Caso práctico: refactorizar el Módulo 2
Vamos a rescatar el backlog canónico de Taller Nómada y reescribir con funciones la lógica que en el Módulo 2 estaba duplicada. Sigue trabajando con arrays paralelos —los objetos y arrays de verdad llegan en el Módulo 4—, pero fíjate en cuánto se aclara el bucle final.
'use strict';
const HOY = '2026-09-20';
const ids = [1, 2, 3, 4, 5, 6];
const titulos = [
'Rediseñar la sala polivalente',
'Cartelería del taller de serigrafía',
'Actualizar la web de reservas',
'Inventario de tintas de serigrafía',
'Guía de encuadernación para residentes',
'Presupuesto de la carpintería'
];
const responsables = ['Iván', 'Marta', 'Lucía', 'Marta', 'Iván', 'Iván'];
const prioridades = ['alta', 'media', 'alta', 'baja', 'media', 'alta'];
const estados = ['en-curso', 'pendiente', 'pendiente', 'hecha', 'en-curso', 'pendiente'];
const horas = [12, 6, 14, 3, 8, 5];
const fechasLimite = [
'2026-09-30', '2026-10-15', '2026-10-02',
'2026-09-12', '2026-11-05', '2026-09-05'
];
// ─── Las piezas con nombre propio ──────────────────────────────────
// Convierte una prioridad en su peso numérico. Regla del negocio.
function pesoDePrioridad(prioridad) {
if (prioridad === 'alta') return 3;
if (prioridad === 'media') return 2;
if (prioridad === 'baja') return 1;
return 0; // prioridad desconocida: no suma
}
// R10: vencida = fecha límite pasada y la tarea no está terminada.
function estaVencida(fechaLimite, estado, hoy) {
return fechaLimite < hoy && estado !== 'hecha';
}
// Símbolo visual del estado.
function marcaDeEstado(estado) {
if (estado === 'hecha') return '✓';
if (estado === 'en-curso') return '▸';
return '○';
}
// Construye la línea de texto de una tarea. NO imprime: devuelve.
function describirTarea(id, titulo, responsable, prioridad, estado, horasEstimadas, fechaLimite, hoy) {
const marca = marcaDeEstado(estado);
const aviso = estaVencida(fechaLimite, estado, hoy) ? ' ⚠ VENCIDA' : '';
return `${marca} [${id}] ${titulo} · ${responsable} · ${prioridad} · ${horasEstimadas} h${aviso}`;
}
// ─── El programa, ahora legible ────────────────────────────────────
let esfuerzoTotal = 0;
for (let i = 0; i < ids.length; i++) {
console.log(
describirTarea(
ids[i], titulos[i], responsables[i], prioridades[i],
estados[i], horas[i], fechasLimite[i], HOY
)
);
esfuerzoTotal += pesoDePrioridad(prioridades[i]) * horas[i];
}
console.log(`Esfuerzo ponderado total: ${esfuerzoTotal}`); // 124Salida:
▸ [1] Rediseñar la sala polivalente · Iván · alta · 12 h ○ [2] Cartelería del taller de serigrafía · Marta · media · 6 h ○ [3] Actualizar la web de reservas · Lucía · alta · 14 h ✓ [4] Inventario de tintas de serigrafía · Marta · baja · 3 h ▸ [5] Guía de encuadernación para residentes · Iván · media · 8 h ○ [6] Presupuesto de la carpintería · Iván · alta · 5 h ⚠ VENCIDA Esfuerzo ponderado total: 124
Compara el cuerpo del bucle con el del Módulo 2. Antes había un switch de veinte líneas, un ternario para la marca y otro para el aviso, todo mezclado. Ahora el bucle tiene dos sentencias y ambas se leen como frases en castellano: "describe la tarea i" y "acumula el peso por las horas".
Y observa el detalle más valioso: describirTarea devuelve el texto en vez de imprimirlo. Eso es lo que permitirá, en el Módulo 6, usar exactamente la misma función para pintar la tarea en la página web sin cambiar ni una línea de su cuerpo.
Sobre esos ocho parámetros: sí, son demasiados. Es el síntoma inevitable de trabajar con arrays paralelos. En Parámetros y Valores de Retorno verás cómo agrupar datos relacionados en un solo argumento, y en el Módulo 4 la tarea pasará a ser un objeto y la firma quedará simplemente
describirTarea(tarea, hoy).
Errores Comunes y Consejos
1. Llamar sin paréntesis.
console.log(pesoDePrioridad); // ƒ pesoDePrioridad(prioridad) { ... }
console.log(pesoDePrioridad('alta')); // 3Si ves algo que empieza por ƒ o [Function: ...] en la consola, te han faltado los paréntesis.
2. Olvidar el return y esperar un valor. Una función sin return devuelve undefined. Si tus cálculos posteriores dan NaN, sospecha de esto antes que de nada.
3. Confundir imprimir con devolver. console.log dentro de la función sirve para depurar, no para comunicar el resultado al resto del programa.
4. Poner código después del return. Nunca se ejecuta. Los editores modernos lo señalan en gris (unreachable code).
5. Salto de línea después de return. Este es un caso especialmente traicionero por la inserción automática de puntos y coma (ASI) que estudiaste en Sintaxis y Conceptos Básicos:
function malRetorno(horas) {
return // ← ASI inserta aquí un ';'
horas * 2; // ← código muerto
}
console.log(malRetorno(12)); // undefinedEl valor que devuelves debe empezar en la misma línea que return.
6. Depender de variables globales en vez de parámetros.
// ✗ Depende de HOY, una variable de fuera: no se puede probar con otra fecha
function estaVencidaMal(fechaLimite, estado) {
return fechaLimite < HOY && estado !== 'hecha';
}
// ✓ Todo lo que necesita entra por parámetros
function estaVencida(fechaLimite, estado, hoy) {
return fechaLimite < hoy && estado !== 'hecha';
}La segunda versión se puede probar con cualquier fecha; la primera solo funciona si existe HOY y siempre da el mismo resultado. Esta idea se formaliza en 03-03 con las funciones puras.
7. Consejo: escribe primero la llamada. Antes de programar el cuerpo, escribe cómo te gustaría usar la función (describirTarea(tarea, hoy)) y qué debería devolver. Diseñar desde el punto de uso produce firmas mucho más cómodas.
8. Consejo: una función, un nivel de detalle. Si dentro de una función mezclas conceptos de negocio (prioridad === 'alta') con detalles de formato ('─'.repeat(40)), es señal de que hay dos funciones dentro.
Ejercicios
Ejercicio 1 — Tres funciones básicas del proyecto
Escribe tres funciones y pruébalas con el backlog:
horasRestantes(horasEstimadas, estado): devuelve0si el estado es'hecha'yhorasEstimadasen cualquier otro caso.etiquetaDeEstado(estado): devuelve'Pendiente','En curso'o'Hecha'; para cualquier otro valor,'Desconocido'.esInminente(fechaLimite, estado, hoy, diasDeMargen): devuelvetruesi la tarea no está hecha y su fecha límite es posterior u igual ahoypero cae dentro de los próximosdiasDeMargendías. Pista: puedes comparar cadenas ISO si construyes el límite superior sumando días conDate.
Ejercicio 2 — Dividir una función que hace demasiado
Esta función viola la responsabilidad única. Divídela en al menos tres funciones con nombres correctos y reescribe el bucle que la usaba.
function informeResponsable(responsable, responsables, horas, estados) {
let total = 0;
let abiertas = 0;
for (let i = 0; i < responsables.length; i++) {
if (responsables[i] === responsable) {
total += horas[i];
if (estados[i] !== 'hecha') abiertas += horas[i];
}
}
const estadoCarga = abiertas > 40 ? 'SOBRECARGA' : 'OK';
console.log(`${responsable}: ${abiertas} h abiertas de ${total} h — ${estadoCarga}`);
}Ejercicio 3 — Traza de la pila
Sin ejecutarlo, escribe el orden exacto en que se imprimen las líneas y dibuja la pila de llamadas en el momento en que se imprime 'C entra'.
function c() { console.log('C entra'); console.log('C sale'); }
function b() { console.log('B entra'); c(); console.log('B sale'); }
function a() { console.log('A entra'); b(); console.log('A sale'); }
a();
console.log('Fin');Soluciones
Ejercicio 1
'use strict';
const HOY = '2026-09-20';
function horasRestantes(horasEstimadas, estado) {
if (estado === 'hecha') return 0;
return horasEstimadas;
}
function etiquetaDeEstado(estado) {
if (estado === 'pendiente') return 'Pendiente';
if (estado === 'en-curso') return 'En curso';
if (estado === 'hecha') return 'Hecha';
return 'Desconocido';
}
function esInminente(fechaLimite, estado, hoy, diasDeMargen) {
if (estado === 'hecha') return false;
if (fechaLimite < hoy) return false; // ya está vencida, no inminente
const limite = new Date(hoy);
limite.setDate(limite.getDate() + diasDeMargen);
const limiteIso = limite.toISOString().slice(0, 10);
return fechaLimite <= limiteIso;
}
console.log(horasRestantes(3, 'hecha')); // 0
console.log(horasRestantes(12, 'en-curso')); // 12
console.log(etiquetaDeEstado('en-curso')); // En curso
console.log(etiquetaDeEstado('archivada')); // Desconocido
console.log(esInminente('2026-09-30', 'en-curso', HOY, 15)); // true
console.log(esInminente('2026-11-05', 'en-curso', HOY, 15)); // false
console.log(esInminente('2026-09-05', 'pendiente', HOY, 15)); // false (vencida)Comentario: cada una de las tres tiene una sola responsabilidad, todas reciben por parámetro todo lo que necesitan (incluido hoy) y ninguna imprime nada. Fíjate en la primera guarda de esInminente: distinguir "vencida" de "inminente" evita que la R10 y el aviso de urgencia se pisen.
Ejercicio 2
// 1. Suma horas de un responsable, con la opción de contar solo las abiertas.
function sumarHoras(responsable, soloAbiertas, responsables, horas, estados) {
let total = 0;
for (let i = 0; i < responsables.length; i++) {
if (responsables[i] !== responsable) continue;
if (soloAbiertas && estados[i] === 'hecha') continue;
total += horas[i];
}
return total;
}
// 2. Aplica la R7: nadie supera 40 h abiertas por semana.
function estaSobrecargado(horasAbiertas) {
return horasAbiertas > 40;
}
// 3. Construye el texto del informe (no imprime).
function describirCarga(responsable, horasAbiertas, horasTotales) {
const marca = estaSobrecargado(horasAbiertas) ? 'SOBRECARGA' : 'OK';
return `${responsable}: ${horasAbiertas} h abiertas de ${horasTotales} h — ${marca}`;
}
const equipo = ['Iván', 'Marta', 'Lucía'];
for (let i = 0; i < equipo.length; i++) {
const abiertas = sumarHoras(equipo[i], true, responsables, horas, estados);
const totales = sumarHoras(equipo[i], false, responsables, horas, estados);
console.log(describirCarga(equipo[i], abiertas, totales));
}
// Iván: 25 h abiertas de 25 h — OK
// Marta: 6 h abiertas de 9 h — OK
// Lucía: 14 h abiertas de 14 h — OKComentario: ahora estaSobrecargado se puede reutilizar en el formulario de asignación, y describirCarga servirá tal cual para pintar una fila en el Módulo 6. El continue es el mismo que aprendiste en break, continue y Bucles Anidados, pero ahora vive dentro de una pieza reutilizable.
Ejercicio 3
Salida:
Pila en el instante de 'C entra' (de arriba abajo): c → b → a → contexto global. Las tres funciones están "abiertas" a la vez; ninguna ha terminado. Las líneas B sale y A sale demuestran que el programa vuelve exactamente al punto donde lo dejó.
Conclusión
Has dado el salto conceptual más importante del curso hasta ahora. Una función es un trozo de lógica con nombre: se declara una vez con function nombre(parámetros) { cuerpo } y se llama tantas veces como haga falta con nombre(argumentos). En cada llamada, los argumentos entran por posición, el cuerpo se ejecuta de arriba abajo y return termina la función entregando un valor; sin return, el valor entregado es undefined.
Ya manejas el vocabulario con precisión —declaración, llamada, parámetro, argumento, cuerpo, retorno— y sabes que el parámetro vive en la plantilla mientras que el argumento cambia en cada llamada. Sabes nombrar funciones como verbo + sustantivo, reservar los prefijos es…/esta…/puede… para las que devuelven booleanos, y detectar por el nombre cuándo una función hace demasiado. Y tienes una intuición sólida de la pila de llamadas: cada llamada apila un contexto y cada return lo desapila, siempre en orden inverso.
Sobre todo, has visto el efecto práctico: la lógica duplicada del Módulo 2 se ha convertido en pesoDePrioridad(), estaVencida(), marcaDeEstado() y describirTarea(), y el bucle que recorre el backlog de Taller Nómada ha pasado de treinta líneas enmarañadas a dos sentencias legibles. El esfuerzo ponderado sigue dando 124, pero ahora la regla que lo produce vive en un solo sitio.
Queda una limitación evidente. Todas las funciones de esta lección son declaraciones con nombre fijo: existen desde que se carga el fichero y siempre se llaman igual. Pero muchas veces necesitarás algo más flexible: guardar una función dentro de una variable, meter varias en una tabla para elegir una según la prioridad, o pasar una función como argumento a otra. Para eso hay que entender que en JavaScript una función es un valor más, como un número o un texto. Eso es lo que verás en Expresiones de Función y Funciones Flecha, donde pesoDePrioridad dejará de ser una declaración suelta para convertirse en una entrada de una tabla de formateadores y validadores de Nómada Tareas.
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
