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

  1. El problema real: el código que se repite
  2. Tu primera función: la declaración
  3. Qué ocurre exactamente en cada llamada
  4. El vocabulario preciso, en una tabla
  5. Parámetros: la misma lógica con datos distintos
  6. return: salir con un valor
  7. Funciones sin parámetros y funciones sin retorno
  8. Cómo nombrar bien una función
  9. La regla de la responsabilidad única
  10. La pila de llamadas, a nivel intuitivo
  11. Caso práctico: refactorizar el Módulo 2
  12. Errores Comunes y Consejos
  13. Ejercicios
  14. Conclusión

  1. 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 5 en lugar de 3, 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.

  1. 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):

function saludarAlEquipo() {
  console.log('Bienvenida al panel de Nómada Tareas.');
}

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 ejecuta

Este 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.

  1. 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:

  1. Los argumentos se evalúan antes de entrar. En pesoDePrioridad(prioridades[i]), primero se resuelve prioridades[i] (por ejemplo, 'alta') y ese valor ya resuelto es el que entra.
  2. 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.
  3. 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.
  4. 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ón

Esa ú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.

  1. 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

  1. 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ía

Dos detalles clave del ejemplo:

  • El orden importa. acortarTitulo(20, 'Rediseñar…') no falla con un mensaje claro: intenta leer .length de un número, obtiene undefined, compara undefined <= 'Rediseñar…' y devuelve algo absurdo. Los argumentos se emparejan por posición, no por nombre.
  • titulo y maximo solo existen dentro del cuerpo. Fuera de la función no están declaradas; si intentas usarlas, obtendrás un ReferenceError.

  1. return: salir con un valor

return hace dos cosas a la vez, y ambas son importantes:

  1. Termina la función inmediatamente. Ninguna línea posterior del cuerpo se ejecuta.
  2. 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));   // sobrecarga

Este 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.

  1. 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
Calcular o transformar pesoDePrioridad('alta')
No Provocar un efecto imprimirTarea(tarea)
No 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);   // undefined

Y 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 + 1

Regla 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.

  1. 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"

  1. 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:

  1. ¿Su nombre necesita una "y"? → divídela.
  2. ¿Tiene más de 20-25 líneas de cuerpo? → probablemente hay dos ideas dentro.
  3. ¿Calcula algo y además lo imprime o lo guarda? → separa el cálculo del efecto.

  1. 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 puntos

Así 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.

  1. 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}`);   // 124

Salida:

▸ [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')); // 3

Si 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));   // undefined

El 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): devuelve 0 si el estado es 'hecha' y horasEstimadas en cualquier otro caso.
  • etiquetaDeEstado(estado): devuelve 'Pendiente', 'En curso' o 'Hecha'; para cualquier otro valor, 'Desconocido'.
  • esInminente(fechaLimite, estado, hoy, diasDeMargen): devuelve true si la tarea no está hecha y su fecha límite es posterior u igual a hoy pero cae dentro de los próximos diasDeMargen días. Pista: puedes comparar cadenas ISO si construyes el límite superior sumando días con Date.

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 — OK

Comentario: 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:

A entra
B entra
C entra
C sale
B sale
A sale
Fin

Pila en el instante de 'C entra' (de arriba abajo): cba → 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

Módulo 2: Estructuras de Control

Módulo 3: Funciones

Módulo 4: Objetos y Arrays

Módulo 5: Objetos y Funciones Avanzadas

Módulo 6: El Modelo de Objetos del Documento (DOM)

Módulo 7: APIs del Navegador y Temas Avanzados

Módulo 8: Pruebas y Depuración

Módulo 9: Rendimiento y Optimización

Módulo 10: Frameworks y Librerías de JavaScript

Módulo 11: Proyecto Final

© Copyright 2026. Todos los derechos reservados