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
- El problema real: el codi que es repeteix
- La teva primera funció: la declaració
- Què passa exactament a cada crida
- El vocabulari precís, en una taula
- Paràmetres: la mateixa lògica amb dades diferents
return: sortir amb un valor- Funcions sense paràmetres i funcions sense retorn
- Com anomenar bé una funció
- La regla de la responsabilitat única
- La pila de crides, a nivell intuïtiu
- Cas pràctic: refactoritzar el Mòdul 2
- Errors Habituals i Consells
- Exercicis
- Conclusió
- 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
5en lloc de3, 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.
- La teva primera funció: la declaració
La manera més bàsica de crear una funció s'anomena declaració de funció (function declaration):
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'executaAquest é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.
- 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:
- Els arguments s'avaluen abans d'entrar. A
pesDePrioritat(prioritats[i]), primer es resolprioritats[i](per exemple,'alta') i aquest valor ja resolt és el que hi entra. - L'execució del codi que crida s'atura mentre la funció treballa, i es reprèn just on era quan la funció acaba.
- 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.
- 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.
- 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
- 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 fusteriaDos detalls clau de l'exemple:
- L'ordre importa.
escurcarTitol(20, 'Redissenyar…')no falla amb un missatge clar: intenta llegir.lengthd'un nombre, obtéundefined, comparaundefined <= 'Redissenyar…'i retorna una cosa absurda. Els arguments s'aparellen per posició, no per nom. titolimaximnomés existeixen dins del cos. Fora de la funció no estan declarades; si les intentes fer servir, obtindràs unReferenceError.
return: sortir amb un valor
return: sortir amb un valorreturn fa dues coses alhora, i totes dues són importants:
- Acaba la funció immediatament. Cap línia posterior del cos no s'executa.
- 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àrregaAquest 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.
- 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 |
|---|---|---|---|
| Sí | Sí | Calcular o transformar | pesDePrioritat('alta') |
| Sí | No | Provocar un efecte | imprimirTasca(tasca) |
| No | Sí | 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); // undefinedI 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 + 1Regla 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.
- 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"
- 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:
- El seu nom necessita una "i"? → divideix-la.
- Té més de 20-25 línies de cos? → probablement hi ha dues idees a dins.
- Calcula alguna cosa i a més la imprimeix o la desa? → separa el càlcul de l'efecte.
- 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 puntsAixí 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.
- 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}`); // 124Sortida:
▸ [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')); // 3Si 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)); // undefinedEl 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): retorna0si l'estat és'feta'ihoresEstimadesen qualsevol altre cas.etiquetaEstat(estat): retorna'Pendent','En curs'o'Feta'; per a qualsevol altre valor,'Desconegut'.esImminent(dataLimit, estat, avui, diesDeMarge): retornatruesi la tasca no està feta i la seva data límit és posterior o igual aavuiperò cau dins dels propersdiesDeMargedies. Pista: pots comparar cadenes ISO si construeixes el límit superior sumant dies ambDate.
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 — OKComentari: 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:
Pila en l'instant de 'C entra' (de dalt a baix): c → b → a → 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
- Què és JavaScript?
- Configuració del teu Entorn de Desenvolupament
- El teu Primer Programa en JavaScript
- Sintaxi i Conceptes Bàsics de JavaScript
- Variables i Tipus de Dades
- Operadors Bàsics
- Conversió de Tipus i Comparacions
- El Projecte del Curs: Nómada Tasques
Mòdul 2: Estructures de Control
- Sentències Condicionals
- Bucles: for, while, do-while
- Sentències Switch
- Control del Flux: break, continue i Bucles Imbricats
- Gestió d'Errors amb try-catch
Mòdul 3: Funcions
- Definició i Crida de Funcions
- Expressions de Funció i Funcions Fletxa
- Paràmetres i Valors de Retorn
- Àmbit i Closures
- Hoisting i el Context d'Execució
- Funcions d'Ordre Superior
- Recursivitat
Mòdul 4: Objectes i Arrays
- Introducció als Objectes
- Mètodes d'Objecte i la Paraula Clau
this - Arrays: Conceptes Bàsics i Mètodes
- Iteració sobre Arrays
- Cercar, Ordenar i Agregar Dades: find, sort i reduce
- Desestructuració d'Arrays
- Desestructuració d'Objectes, Spread i Rest
- JSON i Còpies d'Objectes
Mòdul 5: Objectes i Funcions Avançades
- Prototips i Herència
- Classes i Programació Orientada a Objectes
- Encapsulació: Getters, Setters i Camps Privats
- Mòduls i Importació/Exportació
- JavaScript Asíncron: Callbacks
- Promeses i Async/Await
- El Bucle d'Esdeveniments i la Cua de Microtasques
- Iteradors i Generadors
Mòdul 6: El Model d'Objectes del Document (DOM)
- Introducció al DOM
- Selecció i Manipulació d'Elements del DOM
- Gestió d'Esdeveniments
- Propagació, Delegació i Esdeveniments Personalitzats
- Creació i Eliminació d'Elements del DOM
- Renderitzat de Llistes i Plantilles HTML
- Gestió i Validació de Formularis
Mòdul 7: APIs del Navegador i Temes Avançats
- Emmagatzematge Local i de Sessió
- Fetch API i AJAX
- Peticions Robustes: Errors, Timeouts i AbortController
- WebSockets
- Service Workers i Aplicacions Web Progressives (PWAs)
- APIs del Navegador Essencials
- Introducció a WebAssembly
Mòdul 8: Proves i Depuració
- Depuració de JavaScript
- Qualitat de Codi: ESLint, Prettier i Convencions
- Proves Unitàries amb Jest
- Dobles de Prova: Mocks, Stubs i Spies
- Proves d'Integració
- Proves d'Extrem a Extrem amb Cypress
Mòdul 9: Rendiment i Optimització
- Mesurar Abans d'Optimitzar: DevTools i Web Vitals
- Optimització del Rendiment de JavaScript
- Gestió de Memòria
- Manipulació Eficient del DOM
- Càrrega Diferida i Divisió de Codi
Mòdul 10: Frameworks i Llibreries de JavaScript
- Per Què Existeixen els Frameworks
- Introducció a React
- Gestió d'Estat amb Redux
- Conceptes Bàsics de Vue.js
- Conceptes Bàsics d'Angular
- Triar el Framework Adequat
