Sintaxa modernă pe scurt
Lectură estimată: 16 minute
Estimare pentru text, la un ritm de 180 de cuvinte pe minut. Timpul pentru analizarea codului, exerciții și proiecte se adaugă separat și depinde de ritmul tău.
De la sintaxă memorată la cod pe care îl poți explica
În această lecție vei învăța să citești și să modifici cod JavaScript modern fără să te bazezi pe încercări la întâmplare. Presupunem că ai întâlnit deja variabile, condiții, funcții și obiecte. Dacă poți urmări o funcție care primește două numere și returnează suma lor, ai baza necesară. Nu trebuie să cunoști un framework. Vom lucra cu obiecte mici, produse fictive și funcții pe care le poți executa separat.
Un fragment scurt poate ascunde multe decizii. De exemplu, expresia care afișează prețul unui produs trebuie să decidă dacă zero este acceptat, dacă un preț lipsă înseamnă gratuit și dacă textul „90” trebuie convertit în număr. Sintaxa nu poate lua singură aceste decizii. Ea exprimă regulile pe care le-ai ales. De aceea vom porni de la sensul datelor, apoi vom vedea ce construcție a limbajului exprimă cel mai clar acel sens.
La sfârșit trebuie să poți justifica alegerea dintre const și let, să explici diferența dintre o referință și conținutul unui obiect și să urmărești ce returnează o funcție săgeată. Vei putea de asemenea să alegi între o valoare implicită și o eroare de validare. Aceste abilități apar imediat în formulare, cataloage, calculatoare și răspunsuri primite de la server.
Cum pregătești și urmărești un experiment
Creează un fișier lectie.mjs și rulează-l cu Node.js folosind comanda node lectie.mjs, dacă ai acest mediu instalat. Pentru fragmentele fără importuri poți folosi și consola browserului. Rulează fiecare exemplu separat. Dacă lipești de mai multe ori declarații const cu același nume în același context, poți primi o eroare de redeclarare; aceasta nu demonstrează că exemplul este greșit. Reîncarcă pagina sau folosește un fișier nou pentru experiment.
Înainte să rulezi, notează rezultatul pe care îl aștepți. Apoi compară predicția cu rezultatul observat. Dacă diferența te surprinde, urmărește fiecare expresie, nu modifica mai multe lucruri simultan. Acest obicei transformă consola într-un instrument de învățare: verifici o ipoteză precisă, nu doar cauți o variantă care nu mai afișează eroare.
Ce declară, de fapt, const și let
O variabilă este un nume prin care programul ajunge la o valoare. Cu const declari că legătura dintre nume și valoare nu va fi reasignată. Cu let permiți ca acel nume să primească ulterior altă valoare. Pentru un număr, distincția este ușor de observat: un total declarat cu let poate crește după fiecare poziție; un preț declarat cu const rămâne legat de valoarea inițială în acel domeniu de vizibilitate.
const pretUnitar = 25;
let cantitate = 2;
cantitate = 3;
const total = pretUnitar * cantitate;
console.log(total); // 75Primul rând stabilește prețul. Al doilea stabilește o cantitate care se poate schimba. Al treilea înlocuiește valoarea cantității, iar al patrulea calculează totalul în acel moment. Dacă schimbi cantitate după calcul, total nu se recalculează automat. JavaScript obișnuit nu transformă expresia într-o formulă permanent legată de celulele unui tabel. Pentru un total nou trebuie să execuți din nou calculul sau să apelezi o funcție care îl face.
Această diferență explică multe erori din interfețe: utilizatorul schimbă cantitatea, programul actualizează variabila, dar afișează un rezultat calculat anterior. Problema nu se rezolvă schimbând const în let peste tot. Trebuie să identifici momentul în care se recalculează rezultatul și momentul în care pagina primește rezultatul nou.
Obiectul se poate schimba chiar dacă numele este const
Pentru un obiect, valoarea prin care lucrezi este o referință la acel obiect. Declarația const împiedică reasignarea numelui către alt obiect; ea nu interzice modificarea proprietăților obiectului existent. Dacă ai un produs cu prețul 25 și execuți produs.pret = 30, modificarea poate fi perfect validă chiar dacă produs a fost declarat cu const.
const produs = { nume: 'Caiet', pret: 25 };
const aceeasiReferinta = produs;
produs.pret = 30;
console.log(aceeasiReferinta.pret); // 30
console.log(produs === aceeasiReferinta); // trueNu există două produse în acest exemplu. Cele două nume duc la același obiect. Dacă un formular și o listă folosesc aceeași referință, modificarea poate deveni vizibilă în ambele locuri. Lecția următoare va construi copii controlate, dar înainte de copiere trebuie să recunoști problema. Un nume nou nu înseamnă automat date noi, iar un array nou nu înseamnă automat obiecte noi în interior.
Domeniul de vizibilitate și erorile de inițializare
Declarațiile let și const sunt limitate de blocul în care apar. Un bloc este, de exemplu, corpul unei condiții încadrat de acolade. Dacă declari un mesaj în interiorul condiției și încerci să îl folosești după bloc, numele nu este disponibil acolo. Soluția depinde de intenție: fie afișezi mesajul în acel bloc, fie declari în exterior o variabilă care trebuie să fie disponibilă ambelor ramuri.
Un alt caz este folosirea numelui înainte ca execuția să ajungă la inițializare. Nu presupune că let se comportă ca var din exemplele mai vechi. Poți primi ReferenceError chiar dacă declarația apare câteva rânduri mai jos. Citește eroarea ca pe o informație despre ordinea execuției. Mută calculul după inițializarea datelor de care depinde, în loc să înlocuiești declarația doar ca să ascunzi simptomul.
Într-un proiect existent, schimbarea automată a tuturor declarațiilor var poate modifica comportamentul. Domeniul de funcție și domeniul de bloc nu sunt identice, iar unele închideri folosesc valorile în momente ulterioare. Pentru codul nou din acest curs folosim const și let; pentru migrare, verificăm scenariile care depind de vizibilitate și de momentul accesului.
Funcțiile săgeată: citește ce intră și ce iese
O funcție săgeată este o funcție cu o sintaxă compactă și cu anumite reguli proprii. Pentru început, separă trei lucruri: parametrii, corpul și rezultatul returnat. În expresia n => n * 2, n este parametrul, iar n * 2 este expresia returnată. Apelul cu 4 produce 8. Definirea funcției nu execută însă corpul; trebuie să existe un apel.
const dubleaza = n => n * 2;
const dubleazaExplicit = n => {
const rezultat = n * 2;
return rezultat;
};
console.log(dubleaza(4), dubleazaExplicit(4)); // 8 8În prima variantă, returnarea este implicită pentru expresie. În a doua, acoladele delimitează un bloc de instrucțiuni, deci return trebuie scris. Dacă îl omiți, funcția returnează undefined, chiar dacă a calculat corect o variabilă în interior. Această eroare apare des când o funcție scurtă este extinsă cu un mesaj de diagnostic: adaugi acolade și console.log, dar uiți return.
Funcțiile săgeată nu își creează propriul this. Ele folosesc this din contextul în care au fost definite. Din acest motiv, nu înlocui fiecare metodă de obiect cu o săgeată. O metodă care citește this.pret prin apelul produs.calcul() are altă nevoie decât un callback care transformă un număr. Pentru început, folosește metode obișnuite când ai nevoie de this al apelului și săgeți pentru transformări care primesc date explicit prin parametri.
Șiruri construite clar și obiecte ușor de citit
Template literals folosesc accentul grav și permit inserarea expresiilor prin ${...}. Ele sunt utile pentru mesaje construite din mai multe valori, dar nu validează acele valori. Dacă pret este undefined, mesajul poate conține literalmente undefined. Într-o aplicație, pregătești și validezi datele înainte să construiești textul destinat utilizatorului.
const nume = 'Agendă';
const pret = 40;
const produsNou = { nume, pret };
const mesaj = `${produsNou.nume}: ${produsNou.pret} lei`;
console.log(mesaj); // Agendă: 40 leiÎn obiectul produsNou, forma scurtă { nume, pret } înseamnă că proprietatea nume primește valoarea variabilei nume, iar proprietatea pret primește valoarea variabilei pret. Nu este o legătură dinamică între variabile și proprietăți. Dacă ai nevoie de alt nume pentru o proprietate, îl scrii explicit. Alege nume care păstrează unitățile: pretBani este mai clar decât valoare când calculul folosește bani întregi.
Interpolarea într-un șir nu înseamnă escapare HTML. Dacă introduci textul unui utilizator într-un șablon HTML și îl atribui lui innerHTML, acel text poate fi interpretat ca marcaj. Pentru afișarea unui nume simplu folosește textContent. Separă construirea unui mesaj textual de construirea structurii paginii; ele au cerințe diferite.
Valoare absentă, valoare falsă și valoare invalidă
Zero, șirul gol și false pot fi valori intenționate. Un produs gratuit poate avea preț zero; un întrerupător dezactivat are false; un câmp opțional poate avea șirul gol. Operatorul || folosește alternativa pentru toate valorile falsy, nu doar pentru lipsă. Operatorul ?? folosește alternativa pentru null sau undefined. Alege după semnificația datelor, nu după care variantă produce mai puține caractere.
const configurare = { limita: 0, titlu: '' };
console.log(configurare.limita || 10); // 10
console.log(configurare.limita ?? 10); // 0
console.log(configurare.titlu ?? 'Catalog'); // șir golDacă regula spune că titlul gol nu este acceptat, ?? nu este suficient. Ai nevoie de o verificare a tipului și, eventual, a textului după trim. Același principiu se aplică numerelor: -5 nu este null, dar poate fi un preț invalid. Un fallback rezolvă absența; validarea verifică domeniul permis. Confundarea celor două duce la aplicații care înlocuiesc date greșite cu valori aparent rezonabile.
Optional chaining, scris ?., oprește accesul pe un lanț atunci când baza acelui pas este null sau undefined. Poți citi produs.detalii?.material fără să accesezi proprietatea material pe o valoare absentă. Totuși, variabila produs trebuie să existe, iar rezultatul nu este automat valid. Dacă detalii trebuie obligatoriu furnizat de server, folosirea lui ?. poate ascunde o încălcare a contractului pe care ar trebui să o raportezi.
Cum refactorizezi fără să schimbi regula
Începe prin a scrie câteva intrări reprezentative pentru funcția veche: un preț pozitiv, zero, lipsă și o valoare invalidă. Notează comportamentul cerut, nu doar comportamentul actual. Apoi schimbă o singură categorie de sintaxă, de exemplu declarațiile variabilelor. Rulează aceleași cazuri. Abia după aceea schimbă funcțiile sau construirea șirurilor. Dacă apar diferențe, știi ce schimbare trebuie investigată.
Un cod mai scurt nu este automat mai clar. O expresie condițională cu mai multe alternative imbricate poate fi mai greu de verificat decât două instrucțiuni if. Când logica devine dificil de explicat în cuvinte, introdu o variabilă cu un nume bun sau o funcție mică. Obiectivul este să vezi regula, intrările și rezultatul, inclusiv atunci când revii la cod după câteva săptămâni.
Verificare înainte de laborator
Explică fără să rulezi de ce totalul calculat înainte de schimbarea cantității nu se actualizează singur. Apoi explică de ce schimbarea unei proprietăți este permisă pe un obiect const. În final, construiește un exemplu în care || și ?? produc rezultate diferite și spune care rezultat este corect pentru aplicația imaginară. Dacă poți justifica aceste trei situații, ești pregătit să lucrezi la normalizarea produsului din laboratorul următor.
Studiu de caz: repararea unui calculator de cost
Un coleg îți trimite un calculator în care costul de livrare este ales prin expresia comanda.livrare || 20. În cele mai multe încercări, aplicația pare corectă. Totuși, clienții cu livrare gratuită văd un cost de 20 lei. Prima reacție poate fi să adaugi o excepție pentru acel tip de client. Înainte să modifici codul, formulează însă regula datelor: proprietatea livrare este o sumă numerică, zero înseamnă gratuit, iar absența sumei permite costul standard.
Acum poți identifica precis problema. Operatorul || nu diferențiază zero de celelalte valori falsy. Corectura pentru regula formulată este alegerea fallbackului numai când valoarea este null sau undefined. Dar aceasta este doar prima parte a reparației. Dacă serviciul trimite textul „gratuit” sau numărul -10, ?? le păstrează. Trebuie să decizi dacă accepți asemenea date. În acest caz nu le accepți, deci introduci o validare numerică separată.
Scrie întâi cazurile pe hârtie. Pentru livrare 0, costul trebuie să fie 0. Pentru livrare 15, costul trebuie să fie 15. Pentru proprietate absentă, costul standard este 20. Pentru livrare -10 și pentru textul „0”, funcția trebuie să semnaleze o intrare invalidă. Această listă reprezintă contractul reparației. Nu este necesar să inventezi un sistem mare de teste ca să gândești riguros; este necesar să spui ce comportament verifici.
Urmărește și locul unde este afișat rezultatul
Presupune acum că funcția de calcul produce corect zero, dar interfața afișează în continuare 20 lei. Nu schimba din nou formula. Verifică dacă afișarea aplică propriul fallback, de exemplu costCalculat || 20. Același defect poate exista în două locuri. O regulă importantă este să nu recalculezi interpretarea datelor la fiecare strat. Funcția de calcul produce un rezultat valid, iar afișarea îl formatează fără să îi schimbe sensul.
Verifică apoi dacă mesajul este construit înainte sau după calcul. Un șir precum „Livrare: 20 lei”, salvat mai devreme într-o variabilă, nu se actualizează singur când costul devine zero. Ai nevoie de o nouă execuție a funcției care construiește mesajul. Acesta este același principiu observat la cantitate și total: valorile calculate sunt rezultate ale unei execuții, nu formule vii legate automat de toate variabilele folosite.
Cum explici reparația altui dezvoltator
O explicație bună precizează intrarea care producea defectul, motivul și comportamentul nou. Poți spune: „Pentru livrare 0, operatorul anterior selecta costul standard. Acum zero este păstrat, absența folosește standardul, iar valorile care nu respectă contractul sunt respinse.” Această explicație este mai utilă decât „am modernizat sintaxa”. Ea arată ce poate verifica o altă persoană fără să ghicească intenția modificării.
Păstrează și un caz care nu trebuia să se schimbe, de exemplu livrare 15. Dacă verifici numai cazul defect, poți introduce o corectură care strică situațiile obișnuite. Într-un proiect real, acest tip de comparație înainte și după este baza unei revizuiri de cod. O modificare mică poate avea o justificare precisă fără să devină o explicație complicată.
Exercițiu cu rezolvare explicată: alegerea titlului unui produs
Ai trei produse. Primul are titlul „Caiet”, al doilea are titlul gol, iar al treilea nu are proprietatea titlu. Cerința spune că titlul trebuie să conțină cel puțin un caracter care nu este spațiu; altfel afișăm „Produs fără nume”. Întrebarea este dacă expresia produs.titlu ?? 'Produs fără nume' rezolvă complet cerința. Încearcă să răspunzi înainte de explicația următoare.
Nu o rezolvă complet. Expresia tratează absența, dar păstrează șirul gol și șirul format numai din spații. Soluția trebuie să verifice că valoarea este text, apoi să aplice trim și să verifice lungimea rezultatului. Pentru „Caiet” păstrezi titlul; pentru gol sau spații folosești mesajul de rezervă. Dacă primești un obiect în loc de titlu, nu îl transforma necontrolat în text: decide dacă raportezi eroarea sau folosești fallback conform contractului interfeței.
Observă că aceeași sintaxă poate fi potrivită într-un context și insuficientă în altul. Pentru o descriere opțională, șirul gol poate fi acceptat. Pentru un titlu obligatoriu, nu este. Ceea ce înveți aici este să traduci o cerință în condiții verificabile. Laboratorul de normalizare continuă exact această abordare și păstrează separate extragerea, validarea și prezentarea.
Laborator explicat: normalizarea unui produs
Scopul acestui laborator este să transformi o înregistrare primită din exterior într-un obiect cu care interfața poate lucra predictibil. Creează un fișier laborator.mjs și rulează-l cu Node.js, sau folosește un script de tip module într-o pagină servită local. Exemplele sunt independente de un framework. Datele sunt fictive: un produs gratuit, unul cu preț necunoscut și unul căruia îi lipsește descrierea. Înainte de cod, scrie regula: zero este un preț valid; lipsa prețului trebuie semnalată; o descriere absentă poate primi un text de rezervă.
Separă valoarea absentă de valoarea invalidă
Operatorul pentru valoare implicită nu decide dacă datele sunt corecte. Dacă primești textul „gratuit” în câmpul numeric, înlocuirea lui cu zero ascunde o problemă a contractului. Funcția următoare acceptă numai numere finite, nenegative, și păstrează câmpul necunoscut ca null. Verificarea se face înainte de formatare. Astfel, interfața poate distinge „0 lei” de „preț indisponibil” fără să ghicească dintr-un șir deja formatat.
function normalizeazaProdus(raw) {
if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
throw new TypeError('Produsul trebuie să fie un obiect');
}
const pret = raw.pret ?? null;
if (pret !== null &&
(typeof pret !== 'number' || !Number.isFinite(pret) || pret < 0)) {
throw new TypeError('Preț numeric invalid');
}
const descriere = raw.detalii?.descriere ?? 'Fără descriere';
if (typeof descriere !== 'string') {
throw new TypeError('Descrierea trebuie să fie text');
}
return { pret, descriere };
}
const gratuit = normalizeazaProdus({ pret: 0 });
const necunoscut = normalizeazaProdus({});
console.assert(gratuit.pret === 0);
console.assert(necunoscut.pret === null);
console.assert(gratuit.descriere === 'Fără descriere');Urmărește execuția pentru primul apel. Obiectul trece verificarea inițială, valoarea zero se păstrează, condiția de invaliditate este falsă, iar lipsa detaliilor produce descrierea de rezervă. Pentru al doilea apel, prețul devine null. Nu este aruncată eroare deoarece regula laboratorului permite un preț necunoscut. Dacă aplicația cere obligatoriu un preț, schimbă explicit această regulă și verificarea, în loc să introduci un preț inventat.
Alege declarația după felul în care se schimbă datele
Folosește const pentru o legătură care nu va fi reasignată. Un rezultat calculat o singură dată, o funcție sau o referință la un element din pagină sunt exemple obișnuite. Folosește let când aceeași variabilă primește succesiv alte valori, de exemplu indexul unei căutări. Nu transforma automat fiecare let în const: citește toate atribuțiile. În același timp, nu considera că un obiect declarat const este protejat împotriva modificării proprietăților. Aceasta este o regulă separată a programului.
Returnarea unui obiect dintr-o funcție săgeată
O funcție săgeată cu o expresie returnează acea expresie. Când expresia este un obiect literal, parantezele arată că acoladele descriu obiectul. În varianta cu un bloc de instrucțiuni, ai nevoie de return. Verifică rezultatul în consolă înainte de a conecta funcția la map: un return uitat poate produce o listă de valori undefined, deși numărul de elemente pare corect.
const eticheta = ({ pret }) => ({
text: pret === null ? 'Preț indisponibil' : `${pret} lei`
});
console.assert(eticheta(gratuit).text === '0 lei');
console.assert(eticheta(necunoscut).text === 'Preț indisponibil');Exercițiu cu rezultate verificabile
Adaugă trei apeluri: prețul -1, textul „0” și numărul Infinity. Toate trebuie să producă TypeError în această implementare. Testează separat descrierea goală: aceasta rămâne un șir gol deoarece ai definit fallback numai pentru absență. Decide dacă interfața ta permite această situație și scrie regula înainte să modifici funcția. Nu converti automat orice text cu Number: șirul gol și unele valori fără semnificație de preț pot produce rezultate surprinzătoare.
La autoevaluare, explică de ce gratuit și necunoscut au rezultate diferite, de ce descrierea este validată după extragere și de ce textul prețului este construit într-o funcție separată. Un răspuns complet include intrarea, rezultatul așteptat și motivul. Salvează și un exemplu respins, cu mesajul erorii. Această disciplină face refactorizarea verificabilă: poți schimba sintaxa fără să schimbi accidental sensul datelor.
Documentație pentru aprofundare
Materiale pentru această lecție
Ți-a plăcut lecția? Continuă cursul complet
Ai citit o lecție gratuită. Celelalte lecții, aplicațiile practice și materialele se deblochează odată cu cursul.
149 lei
Plată unică, acces pe viață la toate cele 7 lecții.
Vezi ce conține cursul →