Vas tancar el Mòdul 2 amb una queixa legítima: la cadena que converteix prioritat en un pes l'havies escrita quatre vegades, la validació de transicions apareixia duplicada i el bloc de quaranta línies que valida una tasca nova no servia per validar una tasca editada sense copiar-lo sencer. Aquesta lliçó resol exactament això. Aprendràs a empaquetar un tros de lògica sota un nom, desar-lo i executar-lo tantes vegades com vulguis amb dades diferents cada cop. És el concepte més important del curs fins ara: a partir d'aquí, Nómada Tasques deixa de ser un guió lineal per convertir-se en un conjunt de peces amb nom propi que pots combinar, llegir i —al Mòdul 8— provar per separat.

Contingut

  1. El problema real: el codi que es repeteix
  2. La teva primera funció: la declaració
  3. Què passa exactament a cada crida
  4. El vocabulari precís, en una taula
  5. Paràmetres: la mateixa lògica amb dades diferents
  6. return: sortir amb un valor
  7. Funcions sense paràmetres i funcions sense retorn
  8. Com anomenar bé una funció
  9. La regla de la responsabilitat única
  10. La pila de crides, a nivell intuïtiu
  11. Cas pràctic: refactoritzar el Mòdul 2
  12. Errors Habituals i Consells
  13. Exercicis
  14. Conclusió

  1. El problema real: el codi que es repeteix

Recupera un fragment del Mòdul 2. Aquest bloc converteix una prioritat en un pes numèric per calcular l'esforç ponderat del backlog:

let pes;

if (prioritat === 'alta') {
  pes = 3;
} else if (prioritat === 'mitjana') {
  pes = 2;
} else if (prioritat === 'baixa') {
  pes = 1;
} else {
  pes = 0;
}

Nou línies. El problema no és la longitud: és que aquest bloc exacte apareixia en quatre llocs diferents del codi del mòdul anterior. I això té tres conseqüències molt concretes:

  • El canvi es multiplica. Si demà la Marta decideix que la prioritat alta pesa 5 en lloc de 3, cal tocar quatre llocs. En tocaràs tres i el quart quedarà malament.
  • L'error es multiplica. Si en una de les còpies vas escriure 'mitjanà' amb accent, tens una errada que només es manifesta en una part del programa.
  • La intenció es perd. En llegir el fitxer veus nou línies d'if, no veus "aquí es calcula el pes d'una prioritat". El què queda enterrat sota el com.

Una funció resol les tres coses de cop:

function pesDePrioritat(prioritat) {
  if (prioritat === 'alta') return 3;
  if (prioritat === 'mitjana') return 2;
  if (prioritat === 'baixa') return 1;
  return 0;
}

A partir d'ara, als quatre llocs escrius pesDePrioritat(prioritat). La lògica viu en un sol lloc, es canvia en un sol lloc i el nom diu què fa sense que calgui llegir-ne l'interior.

Una funció és a la lògica el que una variable és a un valor: donar un nom a alguna cosa per poder-hi referir-te sense repetir-la.

  1. La teva primera funció: la declaració

La manera més bàsica de crear una funció s'anomena declaració de funció (function declaration):

function saludarEquip() {
  console.log('Benvinguda al panell de Nómada Tasques.');
}

Peça a peça:

Part A l'exemple Què és
Paraula clau function Diu al motor: "el que ve és una funció"
Nom saludarEquip L'identificador amb què la cridaràs
Parèntesis () Aquí van els paràmetres (aquí, cap)
Claus { ... } Delimiten el cos: el codi que s'executarà

Fixa't en un detall important: escriure aquesta declaració no imprimeix res. Definir una funció és com escriure una recepta en un paper; no es cuina res fins que algú la segueix. Per executar-la cal cridar-la, i cridar-la consisteix a escriure'n el nom seguit de parèntesis:

function saludarEquip() {
  console.log('Benvinguda al panell de Nómada Tasques.');
}

saludarEquip();   // Benvinguda al panell de Nómada Tasques.
saludarEquip();   // Benvinguda al panell de Nómada Tasques.

Els parèntesis són obligatoris i són el que distingeix fer servir la funció d'anomenar-la:

saludarEquip;     // No fa res: és només una referència a la funció
saludarEquip();   // Això sí que l'executa

Aquest és un dels despistes més freqüents en començar. Si crides una funció i "no passa res", el primer que cal mirar són els parèntesis.

Nota sobre l'ordre: les declaracions de funció es poden cridar abans de la línia on estan escrites. El motor les prepara abans d'executar el fitxer. Això s'anomena hoisting i ho estudiaràs amb detall a Hoisting i el Context d'Execució; de moment, escriu les funcions a dalt i les crides a baix, que és el més llegible.

  1. Què passa exactament a cada crida

Quan escrius pesDePrioritat('alta'), el motor fa aquesta seqüència:

sequenceDiagram
    participant P as Codi principal
    participant F as pesDePrioritat
    P->>P: Avalua l'argument 'alta'
    P->>F: Salta al cos amb prioritat = 'alta'
    Note over F: Executa el cos<br/>línia a línia
    F-->>P: return 3
    Note over P: La crida se substitueix<br/>pel valor 3
    P->>P: Continua a la línia següent

Quatre idees que convé tenir molt clares des del principi:

  1. Els arguments s'avaluen abans d'entrar. A pesDePrioritat(prioritats[i]), primer es resol prioritats[i] (per exemple, 'alta') i aquest valor ja resolt és el que hi entra.
  2. L'execució del codi que crida s'atura mentre la funció treballa, i es reprèn just on era quan la funció acaba.
  3. Cada crida és independent. Les variables declarades dins del cos neixen en entrar-hi i desapareixen en sortir-ne. Dues crides no comparteixen res. Això ho formalitzaràs a Àmbit i Closures.
  4. Una crida és una expressió: es pot fer servir a qualsevol lloc on hi encaixi un valor.
const pes = pesDePrioritat('mitjana');                    // en una assignació
console.log(`Pes: ${pesDePrioritat('alta')}`);            // dins d'un template literal
if (pesDePrioritat(prioritats[0]) >= 3) { /* ... */ }     // dins d'una condició
const esforc = pesDePrioritat('alta') * 12;               // dins d'una operació

Aquesta darrera línia val la pena llegir-la a poc a poc: la crida retorna 3, l'expressió es converteix en 3 * 12 i esforc acaba valent 36. La funció se substitueix pel que retorna.

  1. El vocabulari precís, en una taula

En parlar de funcions es fan servir sis termes que convé no confondre, perquè els missatges d'error i la documentació els fan servir amb precisió:

Terme Què és A l'exemple
Declaració El codi que crea la funció function pesDePrioritat(prioritat) { ... }
Nom L'identificador de la funció pesDePrioritat
Paràmetre La variable declarada entre parèntesis a la definició prioritat
Argument El valor real que passes en cridar-la 'alta' a pesDePrioritat('alta')
Cos El bloc entre claus que s'executa Els quatre if/return
Crida / invocació L'acte d'executar-la pesDePrioritat('alta')
Valor de retorn El que la crida produeix 3

La distinció paràmetre / argument és la que més es confon i la més útil. Una regla mnemotècnica: el paràmetre és a la plantilla (la definició); l'argument és el valor actual d'una crida concreta.

//                          ↓ paràmetre (existeix un cop, a la definició)
function esTascaVencuda(dataLimit, estat, avui) {
  return dataLimit < avui && estat !== 'feta';
}

//                ↓ arguments (diferents a cada crida)
esTascaVencuda('2026-09-05', 'pendent', '2026-09-20');   // true
esTascaVencuda('2026-11-05', 'en-curs', '2026-09-20');   // false

  1. Paràmetres: la mateixa lògica amb dades diferents

Els paràmetres són el que converteix una funció en una eina reutilitzable en lloc d'una simple drecera. Es declaren separats per comes i es comporten com a variables locals que ja vénen amb valor:

// Retorna el títol retallat a un nombre màxim de caràcters.
function escurcarTitol(titol, maxim) {
  if (titol.length <= maxim) {
    return titol;
  }
  return titol.slice(0, maxim - 1) + '…';
}

console.log(escurcarTitol('Redissenyar la sala polivalent', 20));
// Redissenyar la sala…
console.log(escurcarTitol('Pressupost de la fusteria', 40));
// Pressupost de la fusteria

Dos detalls clau de l'exemple:

  • L'ordre importa. escurcarTitol(20, 'Redissenyar…') no falla amb un missatge clar: intenta llegir .length d'un nombre, obté undefined, compara undefined <= 'Redissenyar…' i retorna una cosa absurda. Els arguments s'aparellen per posició, no per nom.
  • titol i maxim només existeixen dins del cos. Fora de la funció no estan declarades; si les intentes fer servir, obtindràs un ReferenceError.

  1. return: sortir amb un valor

return fa dues coses alhora, i totes dues són importants:

  1. Acaba la funció immediatament. Cap línia posterior del cos no s'executa.
  2. Lliura un valor al punt des d'on s'ha cridat.
function classificarCarrega(hores) {
  if (hores > 40) return 'sobrecàrrega';
  if (hores > 30) return 'alta';
  if (hores > 15) return 'normal';
  return 'folgada';
  console.log('Aixo no arriba a executar-se mai');   // codi mort
}

console.log(classificarCarrega(25));   // normal
console.log(classificarCarrega(48));   // sobrecàrrega

Aquest estil —una cadena de return en lloc d'un if/else if/else amb una variable temporal— és la versió de funció de les clàusules de guarda que vas aprendre a Sentències Condicionals. Cada línia es llegeix com una regla independent, sense imbricació.

A Paràmetres i Valors de Retorn aprofundiràs en què retornar, com retornar diverses dades alhora i per què convé retornar sempre el mateix tipus. De moment n'hi ha prou amb la mecànica.

  1. Funcions sense paràmetres i funcions sense retorn

No totes les funcions reben dades ni totes retornen alguna cosa. Les quatre combinacions són legítimes i es fan servir per a coses diferents:

Rep Retorna Per a què serveix Exemple
Calcular o transformar pesDePrioritat('alta')
No Provocar un efecte imprimirTasca(tasca)
No Produir alguna cosa nova cada cop dataAvui()
No No Executar una rutina fixa mostrarCapcalera()
// Sense paràmetres, amb retorn: sempre produeix el mateix format
function dataAvui() {
  return '2026-09-20';   // al projecte fem servir una data fixa per poder raonar
}

// Amb paràmetres, sense retorn: el seu valor és a l'efecte que produeix
function imprimirSeparador(titol) {
  console.log('');
  console.log(`── ${titol} ${'─'.repeat(40 - titol.length)}`);
}

imprimirSeparador('Backlog de Taller Nómada');

Una funció sense return no retorna "res": retorna undefined. Això és font de confusió, així que comprova-ho:

const resultat = imprimirSeparador('Prova');
console.log(resultat);          // undefined
console.log(typeof resultat);   // undefined

I compte amb el parany clàssic: una funció que fa console.log no retorna el que imprimeix.

function malCalcul(hores) {
  console.log(hores * 2);        // imprimeix, però no retorna
}

const doble = malCalcul(12);     // imprimeix 24
console.log(doble + 1);          // NaN  ← undefined + 1

Regla pràctica: console.log és per mirar; return és per fer servir. Si el valor l'ha de consumir una altra part del programa, ha de sortir per return.

  1. Com anomenar bé una funció

El nom d'una funció és documentació de franc. La convenció universal en JavaScript és verb + substantiu, en camelCase:

Tipus de funció Patró de nom Exemples del projecte
Calcula i retorna un valor calcular…, obtenir…, comptar… calcularUrgencia, obtenirCarregaDe, comptarPendents
Retorna un booleà es…, esta…, te…, pot… esTascaVencuda, estaCompleta, teEtiqueta, potCanviarEstat
Transforma una dada en un altre format formatar…, descriure…, a… formatarData, descriureTasca, aMinuscules
Provoca un efecte imprimir…, desar…, registrar… imprimirBacklog, desarTasca, registrarCanvi
Comprova i llança error si falla validar…, assegurar… validarTasca, assegurarHoresValides

I els noms que cal evitar, amb el perquè:

Nom dolent Problema
ferCosa(), processar() No diu què fa; qualsevol codi hi cap a dins
dades(), tasca() Un substantiu solt sembla una variable, no una acció
check() Ambigu: retorna un booleà o llança un error?
calcularUrgenciaIDesar() La conjunció "i" avisa que fa dues coses (vegeu l'apartat 9)
fn2(), aux() Noms sense significat; d'aquí a tres setmanes ningú no sabrà què són

Un matís que estalvia molts errors: les funcions que retornen un booleà s'anomenen com una pregunta afirmativa, perquè en fer-les servir dins d'un if es llegeixin com una frase.

if (esTascaVencuda('2026-09-05', 'pendent', AVUI)) {
  console.warn('R10: tasca vençuda.');
}
// es llegeix: "si és tasca vençuda, avisa"

  1. La regla de la responsabilitat única

Una funció ha de fer una sola cosa i ha de poder explicar-se en una frase sense la paraula "i". Aquest és un exemple real del que passa quan s'ignora la regla:

// ✗ Fa quatre coses: calcular, formatar, imprimir i avisar
function processarTasca(titol, prioritat, dataLimit, estat, hores, avui) {
  let pes;
  if (prioritat === 'alta') pes = 3;
  else if (prioritat === 'mitjana') pes = 2;
  else if (prioritat === 'baixa') pes = 1;
  else pes = 0;

  const esforc = pes * hores;
  const vencuda = dataLimit < avui && estat !== 'feta';
  const marca = estat === 'feta' ? '✓' : '○';

  console.log(`${marca} ${titol} · esforç ${esforc}`);
  if (vencuda) console.warn(`⚠ ${titol} està vençuda`);
}

Aquesta funció no es pot reutilitzar per a res més: si només vols l'esforç, sense imprimir, no pots. Si vols saber si està vençuda per pintar-la de vermell al Mòdul 6, tampoc. Està tot soldat.

Ara la mateixa lògica repartida en peces:

flowchart TD
    A["processarTasca (tot junt)"] --> B["pesDePrioritat<br/>prioritat → nombre"]
    A --> C["estaVencuda<br/>data + estat → booleà"]
    A --> D["descriureTasca<br/>dades → text"]
    A --> E["imprimirTasca<br/>text → consola"]
    B --> F["Reutilitzable en el<br/>càlcul d'esforç"]
    C --> G["Reutilitzable en els<br/>avisos i en el filtre"]
    D --> H["Reutilitzable a la<br/>consola i al DOM"]

Tres preguntes per saber si una funció té massa responsabilitats:

  1. El seu nom necessita una "i"? → divideix-la.
  2. Té més de 20-25 línies de cos? → probablement hi ha dues idees a dins.
  3. Calcula alguna cosa i a més la imprimeix o la desa? → separa el càlcul de l'efecte.

  1. La pila de crides, a nivell intuïtiu

Quan una funció en crida una altra, el motor ha de recordar on ha de tornar. Ho fa amb una estructura anomenada pila de crides (call stack): una pila de plats on cada crida n'afegeix un a sobre i cada return en retira el de dalt.

function pesDePrioritat(prioritat) {
  if (prioritat === 'alta') return 3;
  if (prioritat === 'mitjana') return 2;
  if (prioritat === 'baixa') return 1;
  return 0;
}

function esforcDeTasca(prioritat, hores) {
  return pesDePrioritat(prioritat) * hores;
}

function informeDeTasca(titol, prioritat, hores) {
  const esforc = esforcDeTasca(prioritat, hores);
  return `${titol}: ${esforc} punts`;
}

console.log(informeDeTasca('Redissenyar la sala polivalent', 'alta', 12));
// Redissenyar la sala polivalent: 36 punts

Així creix i es buida la pila durant aquesta única línia:

flowchart LR
    subgraph P1["1 · Crida inicial"]
        A1["informeDeTasca"]
        A0["(global)"]
    end
    subgraph P2["2 · Entra esforcDeTasca"]
        B2["esforcDeTasca"]
        B1["informeDeTasca"]
        B0["(global)"]
    end
    subgraph P3["3 · Entra pesDePrioritat"]
        C3["pesDePrioritat"]
        C2["esforcDeTasca"]
        C1["informeDeTasca"]
        C0["(global)"]
    end
    subgraph P4["4 · Tot ha tornat"]
        D0["(global)"]
    end
    P1 --> P2 --> P3 --> P4

El que importa avui és la intuïció: l'últim que entra és el primer que surt, i el programa sempre torna exactament al punt on s'havia quedat. Aquesta pila és també la que apareix als missatges d'error del Mòdul 2 (at pesDePrioritat, at esforcDeTasca, …): el stack d'un Error és literalment una foto d'aquesta pila. A Hoisting i el Context d'Execució en veuràs el mecanisme per dins i què passa quan la pila s'omple.

  1. Cas pràctic: refactoritzar el Mòdul 2

Rescatarem el backlog canònic de Taller Nómada i reescriurem amb funcions la lògica que al Mòdul 2 estava duplicada. Continues treballant amb arrays paral·lels —els objectes i arrays de debò arriben al Mòdul 4—, però fixa't en com s'aclareix el bucle final.

'use strict';

const AVUI = '2026-09-20';

const ids          = [1, 2, 3, 4, 5, 6];
const titols       = [
  'Redissenyar la sala polivalent',
  'Cartelleria del taller de serigrafia',
  'Actualitzar el web de reserves',
  'Inventari de tintes de serigrafia',
  'Guia sobre enquadernació per a residents',
  'Pressupost de la fusteria'
];
const responsables = ['Iván', 'Marta', 'Lucía', 'Marta', 'Iván', 'Iván'];
const prioritats   = ['alta', 'mitjana', 'alta', 'baixa', 'mitjana', 'alta'];
const estats       = ['en-curs', 'pendent', 'pendent', 'feta', 'en-curs', 'pendent'];
const hores        = [12, 6, 14, 3, 8, 5];
const datesLimit   = [
  '2026-09-30', '2026-10-15', '2026-10-02',
  '2026-09-12', '2026-11-05', '2026-09-05'
];

// ─── Les peces amb nom propi ───────────────────────────────────────

// Converteix una prioritat en el seu pes numèric. Regla de negoci.
function pesDePrioritat(prioritat) {
  if (prioritat === 'alta') return 3;
  if (prioritat === 'mitjana') return 2;
  if (prioritat === 'baixa') return 1;
  return 0;   // prioritat desconeguda: no suma
}

// R10: vençuda = data límit passada i la tasca no està acabada.
function estaVencuda(dataLimit, estat, avui) {
  return dataLimit < avui && estat !== 'feta';
}

// Símbol visual de l'estat.
function marcaEstat(estat) {
  if (estat === 'feta') return '✓';
  if (estat === 'en-curs') return '▸';
  return '○';
}

// Construeix la línia de text d'una tasca. NO imprimeix: retorna.
function descriureTasca(id, titol, responsable, prioritat, estat, horesEstimades, dataLimit, avui) {
  const marca = marcaEstat(estat);
  const avis = estaVencuda(dataLimit, estat, avui) ? ' ⚠ VENÇUDA' : '';
  return `${marca} [${id}] ${titol} · ${responsable} · ${prioritat} · ${horesEstimades} h${avis}`;
}

// ─── El programa, ara llegible ─────────────────────────────────────

let esforcTotal = 0;

for (let i = 0; i < ids.length; i++) {
  console.log(
    descriureTasca(
      ids[i], titols[i], responsables[i], prioritats[i],
      estats[i], hores[i], datesLimit[i], AVUI
    )
  );
  esforcTotal += pesDePrioritat(prioritats[i]) * hores[i];
}

console.log(`Esforç ponderat total: ${esforcTotal}`);   // 124

Sortida:

▸ [1] Redissenyar la sala polivalent · Iván · alta · 12 h
○ [2] Cartelleria del taller de serigrafia · Marta · mitjana · 6 h
○ [3] Actualitzar el web de reserves · Lucía · alta · 14 h
✓ [4] Inventari de tintes de serigrafia · Marta · baixa · 3 h
▸ [5] Guia sobre enquadernació per a residents · Iván · mitjana · 8 h
○ [6] Pressupost de la fusteria · Iván · alta · 5 h ⚠ VENÇUDA
Esforç ponderat total: 124

Compara el cos del bucle amb el del Mòdul 2. Abans hi havia un switch de vint línies, un ternari per a la marca i un altre per a l'avís, tot barrejat. Ara el bucle té dues sentències i totes dues es llegeixen com a frases en català: "descriu la tasca i" i "acumula el pes per les hores".

I observa el detall més valuós: descriureTasca retorna el text en comptes d'imprimir-lo. Això és el que permetrà, al Mòdul 6, fer servir exactament la mateixa funció per pintar la tasca a la pàgina web sense canviar ni una línia del seu cos.

Sobre aquests vuit paràmetres: sí, són massa. És el símptoma inevitable de treballar amb arrays paral·lels. A Paràmetres i Valors de Retorn veuràs com agrupar dades relacionades en un sol argument, i al Mòdul 4 la tasca passarà a ser un objecte i la signatura quedarà simplement descriureTasca(tasca, avui).

Errors Habituals i Consells

1. Cridar sense parèntesis.

console.log(pesDePrioritat);         // ƒ pesDePrioritat(prioritat) { ... }
console.log(pesDePrioritat('alta')); // 3

Si veus alguna cosa que comença per ƒ o [Function: ...] a la consola, t'han faltat els parèntesis.

2. Oblidar el return i esperar un valor. Una funció sense return retorna undefined. Si els teus càlculs posteriors donen NaN, sospita d'això abans que de res.

3. Confondre imprimir amb retornar. console.log dins de la funció serveix per depurar, no per comunicar el resultat a la resta del programa.

4. Posar codi després del return. No s'executa mai. Els editors moderns ho assenyalen en gris (unreachable code).

5. Salt de línia després de return. Aquest cas és especialment traïdor per la inserció automàtica de punts i comes (ASI) que vas estudiar a Sintaxi i Conceptes Bàsics:

function malRetorn(hores) {
  return                  // ← ASI hi insereix un ';'
    hores * 2;            // ← codi mort
}
console.log(malRetorn(12));   // undefined

El valor que retornes ha de començar a la mateixa línia que return.

6. Dependre de variables globals en lloc de paràmetres.

// ✗ Depèn d'AVUI, una variable de fora: no es pot provar amb una altra data
function estaVencudaMal(dataLimit, estat) {
  return dataLimit < AVUI && estat !== 'feta';
}

// ✓ Tot el que necessita hi entra per paràmetres
function estaVencuda(dataLimit, estat, avui) {
  return dataLimit < avui && estat !== 'feta';
}

La segona versió es pot provar amb qualsevol data; la primera només funciona si existeix AVUI i sempre dóna el mateix resultat. Aquesta idea es formalitza a 03-03 amb les funcions pures.

7. Consell: escriu primer la crida. Abans de programar el cos, escriu com t'agradaria fer servir la funció (descriureTasca(tasca, avui)) i què hauria de retornar. Dissenyar des del punt d'ús produeix signatures molt més còmodes.

8. Consell: una funció, un nivell de detall. Si dins d'una funció barreges conceptes de negoci (prioritat === 'alta') amb detalls de format ('─'.repeat(40)), és senyal que hi ha dues funcions a dins.

Exercicis

Exercici 1 — Tres funcions bàsiques del projecte

Escriu tres funcions i prova-les amb el backlog:

  • horesRestants(horesEstimades, estat): retorna 0 si l'estat és 'feta' i horesEstimades en qualsevol altre cas.
  • etiquetaEstat(estat): retorna 'Pendent', 'En curs' o 'Feta'; per a qualsevol altre valor, 'Desconegut'.
  • esImminent(dataLimit, estat, avui, diesDeMarge): retorna true si la tasca no està feta i la seva data límit és posterior o igual a avui però cau dins dels propers diesDeMarge dies. Pista: pots comparar cadenes ISO si construeixes el límit superior sumant dies amb Date.

Exercici 2 — Dividir una funció que fa massa coses

Aquesta funció incompleix la responsabilitat única. Divideix-la en almenys tres funcions amb noms correctes i reescriu el bucle que la feia servir.

function informeResponsable(responsable, responsables, hores, estats) {
  let total = 0;
  let obertes = 0;
  for (let i = 0; i < responsables.length; i++) {
    if (responsables[i] === responsable) {
      total += hores[i];
      if (estats[i] !== 'feta') obertes += hores[i];
    }
  }
  const estatCarrega = obertes > 40 ? 'SOBRECÀRREGA' : 'OK';
  console.log(`${responsable}: ${obertes} h obertes de ${total} h — ${estatCarrega}`);
}

Exercici 3 — Traça de la pila

Sense executar-lo, escriu l'ordre exacte en què s'imprimeixen les línies i dibuixa la pila de crides en el moment en què s'imprimeix 'C entra'.

function c() { console.log('C entra'); console.log('C surt'); }
function b() { console.log('B entra'); c(); console.log('B surt'); }
function a() { console.log('A entra'); b(); console.log('A surt'); }
a();
console.log('Fi');

Solucions

Exercici 1

'use strict';

const AVUI = '2026-09-20';

function horesRestants(horesEstimades, estat) {
  if (estat === 'feta') return 0;
  return horesEstimades;
}

function etiquetaEstat(estat) {
  if (estat === 'pendent') return 'Pendent';
  if (estat === 'en-curs') return 'En curs';
  if (estat === 'feta') return 'Feta';
  return 'Desconegut';
}

function esImminent(dataLimit, estat, avui, diesDeMarge) {
  if (estat === 'feta') return false;
  if (dataLimit < avui) return false;              // ja està vençuda, no imminent

  const limit = new Date(avui);
  limit.setDate(limit.getDate() + diesDeMarge);
  const limitIso = limit.toISOString().slice(0, 10);

  return dataLimit <= limitIso;
}

console.log(horesRestants(3, 'feta'));                              // 0
console.log(horesRestants(12, 'en-curs'));                          // 12
console.log(etiquetaEstat('en-curs'));                              // En curs
console.log(etiquetaEstat('arxivada'));                             // Desconegut
console.log(esImminent('2026-09-30', 'en-curs', AVUI, 15));         // true
console.log(esImminent('2026-11-05', 'en-curs', AVUI, 15));         // false
console.log(esImminent('2026-09-05', 'pendent', AVUI, 15));         // false (vençuda)

Comentari: cadascuna de les tres té una sola responsabilitat, totes reben per paràmetre tot el que necessiten (inclòs avui) i cap no imprimeix res. Fixa't en la primera guarda d'esImminent: distingir "vençuda" d'"imminent" evita que la R10 i l'avís d'urgència es trepitgin.

Exercici 2

// 1. Suma hores d'un responsable, amb l'opció de comptar només les obertes.
function sumarHores(responsable, nomesObertes, responsables, hores, estats) {
  let total = 0;
  for (let i = 0; i < responsables.length; i++) {
    if (responsables[i] !== responsable) continue;
    if (nomesObertes && estats[i] === 'feta') continue;
    total += hores[i];
  }
  return total;
}

// 2. Aplica la R7: ningú no supera 40 h obertes per setmana.
function estaSobrecarregat(horesObertes) {
  return horesObertes > 40;
}

// 3. Construeix el text de l'informe (no imprimeix).
function descriureCarrega(responsable, horesObertes, horesTotals) {
  const marca = estaSobrecarregat(horesObertes) ? 'SOBRECÀRREGA' : 'OK';
  return `${responsable}: ${horesObertes} h obertes de ${horesTotals} h — ${marca}`;
}

const equip = ['Iván', 'Marta', 'Lucía'];

for (let i = 0; i < equip.length; i++) {
  const obertes = sumarHores(equip[i], true, responsables, hores, estats);
  const totals  = sumarHores(equip[i], false, responsables, hores, estats);
  console.log(descriureCarrega(equip[i], obertes, totals));
}
// Iván: 25 h obertes de 25 h — OK
// Marta: 6 h obertes de 9 h — OK
// Lucía: 14 h obertes de 14 h — OK

Comentari: ara estaSobrecarregat es pot reutilitzar al formulari d'assignació, i descriureCarrega servirà tal com està per pintar una fila al Mòdul 6. El continue és el mateix que vas aprendre a break, continue i Bucles Imbricats, però ara viu dins d'una peça reutilitzable.

Exercici 3

Sortida:

A entra
B entra
C entra
C surt
B surt
A surt
Fi

Pila en l'instant de 'C entra' (de dalt a baix): cba → context global. Les tres funcions estan "obertes" alhora; cap no ha acabat. Les línies B surt i A surt demostren que el programa torna exactament al punt on l'havia deixat.

Conclusió

Has fet el salt conceptual més important del curs fins ara. Una funció és un tros de lògica amb nom: es declara un cop amb function nom(paràmetres) { cos } i es crida tantes vegades com calgui amb nom(arguments). A cada crida, els arguments hi entren per posició, el cos s'executa de dalt a baix i return acaba la funció lliurant un valor; sense return, el valor lliurat és undefined.

Ja manegues el vocabulari amb precisió —declaració, crida, paràmetre, argument, cos, retorn— i saps que el paràmetre viu a la plantilla mentre que l'argument canvia a cada crida. Saps anomenar funcions com a verb + substantiu, reservar els prefixos es…/esta…/pot… per a les que retornen booleans, i detectar pel nom quan una funció fa massa coses. I tens una intuïció sòlida de la pila de crides: cada crida hi apila un context i cada return el desapila, sempre en ordre invers.

Sobretot, n'has vist l'efecte pràctic: la lògica duplicada del Mòdul 2 s'ha convertit en pesDePrioritat(), estaVencuda(), marcaEstat() i descriureTasca(), i el bucle que recorre el backlog de Taller Nómada ha passat de trenta línies embolicades a dues sentències llegibles. L'esforç ponderat continua donant 124, però ara la regla que el produeix viu en un sol lloc.

Queda una limitació evident. Totes les funcions d'aquesta lliçó són declaracions amb nom fix: existeixen des que es carrega el fitxer i sempre es criden igual. Però moltes vegades necessitaràs alguna cosa més flexible: desar una funció dins d'una variable, ficar-ne diverses en una taula per triar-ne una segons la prioritat, o passar una funció com a argument a una altra. Per a això cal entendre que en JavaScript una funció és un valor més, com un nombre o un text. Això és el que veuràs a Expressions de Funció i Funcions Fletxa, on pesDePrioritat deixarà de ser una declaració solta per convertir-se en una entrada d'una taula de formatadors i validadors de Nómada Tasques.

Curs de JavaScript: De Principiant a Avançat

Mòdul 1: Introducció a JavaScript

Mòdul 2: Estructures de Control

Mòdul 3: Funcions

Mòdul 4: Objectes i Arrays

Mòdul 5: Objectes i Funcions Avançades

Mòdul 6: El Model d'Objectes del Document (DOM)

Mòdul 7: APIs del Navegador i Temes Avançats

Mòdul 8: Proves i Depuració

Mòdul 9: Rendiment i Optimització

Mòdul 10: Frameworks i Llibreries de JavaScript

Mòdul 11: Projecte Final

© Copyright 2026. Tots els drets reservats