Creșterea Șanselor Profesionale cu JavaScript
Deținerea cunoștințelor de JavaScript îți sporește considerabil șansele de a obține o poziție de dezvoltator software. În acest context, vom analiza întrebările cele mai frecvente în interviurile de angajare pentru dezvoltatori JavaScript.
JavaScript este un limbaj de programare extrem de popular în dezvoltarea web, fiind utilizat pentru a crea diverse tipuri de aplicații.
Înainte de a explora întrebările tipice de interviu, să analizăm avantajele învățării acestui limbaj de programare.
JavaScript este un limbaj de programare interpretat, ușor de învățat, fiind unul dintre pilonii de bază ai World Wide Web. Te încurajăm să te familiarizezi și cu celelalte două limbaje esențiale ale web-ului, dacă nu ai făcut-o deja.
Deși JavaScript a fost creat inițial pentru web, acum, datorită mediilor de execuție precum Node.js și Deno, poate fi folosit pe diverse platforme.
Să explorăm câteva beneficii majore ale utilizării JavaScript:
Beneficiile JavaScript
- Ușor de început. Poți învăța JavaScript chiar dacă nu ai experiență anterioară în programare.
- Comunitate mare și activă. Vei găsi sprijin rapid în cazul în care întâmpini dificultăți.
- O multitudine de biblioteci și framework-uri construite pe JavaScript facilitează dezvoltarea rapidă a aplicațiilor.
- Cu JavaScript poți dezvolta aplicații frontend, backend, aplicații pentru Android și iOS, fiind un limbaj versatil, cu precădere în domeniul dezvoltării web.
Tipurile de Date în JavaScript
Tipurile de date sunt folosite pentru a stoca diverse tipuri de informații. Tipurile de date diferă de la un limbaj de programare la altul. JavaScript oferă 8 tipuri de date:
- Număr (Number)
- Șir de caractere (String)
- Boolean
- Nedefinit (Undefined)
- Nul (Null)
- BigInt
- Simbol (Symbol)
- Obiect (Object)
Toate tipurile de date, cu excepția obiectului, sunt considerate valori primitive, fiind imutabile.
Metode Încorporate în JavaScript
JavaScript oferă metode predefinite pentru fiecare tip de date. Aceste metode pot fi accesate folosind tipul de date corespunzător. Iată câteva exemple de metode pentru diferite tipuri de date:
- Număr
- Șir de caractere
- toLowerCase
- startsWith
- charAt
- Matrice (Array)
Există o multitudine de metode disponibile pentru fiecare tip de date. Poți consulta documentația pentru o listă completă a metodelor disponibile pentru fiecare tip și structură de date.
Crearea Matricelor în JavaScript
Matricele sunt structuri de date fundamentale în JavaScript. Ele pot conține orice tip de date, datorită naturii dinamice a limbajului. Iată cum poți crea matrice în JavaScript:
Poți crea o matrice folosind paranteze drepte []. Aceasta este o metodă rapidă și ușoară pentru a crea matrice:
// Matrice goală const arr = []; // Matrice cu valori aleatorii const randomArr = [1, "Unu", true]; console.log(arr, randomArr);
De asemenea, matricele pot fi create folosind constructorul Array, dar aceasta metodă este mai puțin utilizată în proiectele obișnuite:
// Matrice goală const arr = new Array(); // Matrice cu valori aleatorii const randomArr = new Array(1, "Unu", true); console.log(arr, randomArr);
Matricele JavaScript sunt mutabile, adică pot fi modificate după ce au fost create.
Crearea Obiectelor în JavaScript
Pe lângă matrice, obiectele sunt o altă structură de date de bază în JavaScript. Obiectele stochează perechi cheie-valoare, unde cheia trebuie să fie o valoare imutabilă, iar valoarea poate fi orice tip de date. Iată cum creezi obiecte în JavaScript:
Poți crea obiecte folosind acolade {}. Aceasta este o metodă simplă și rapidă:
// Obiect gol const object = {}; // Obiect cu valori aleatorii const randomObject = { 1: 2, unu: "Doi", true: false }; console.log(object, randomObject);
Obiectele pot fi create și folosind constructorul Object, dar aceasta metodă este mai puțin frecventă în proiecte:
// Obiect gol const object = new Object(); // Obiect cu valori aleatorii const randomObject = new Object(); randomObject[1] = 2; randomObject["unu"] = "Doi"; randomObject[true] = false; console.log(object, randomObject);
Obiectele JavaScript sunt mutabile, putând fi modificate după creare, după cum se vede în al doilea exemplu.
Depanarea Codului JavaScript
Depanarea codului poate fi un proces complex, care diferă de la un limbaj la altul și de la un proiect la altul. Vom examina metodele frecvent utilizate pentru depanarea codului JavaScript:
1. Utilizarea Consolei
Poți folosi `console.log()` în diverse locuri din cod pentru a identifica erorile. Execuția codului se oprește când se întâlnește o eroare.
Logarea este o metodă clasică de depanare, eficientă în special pentru proiectele mici. Este o tehnică de bază în multe limbaje de programare.
2. Instrumentele pentru Dezvoltatori
JavaScript este folosit extensiv în dezvoltarea web, iar majoritatea browserelor includ instrumente de dezvoltare utile pentru depanarea codului JavaScript.
O metodă populară este setarea punctelor de întrerupere (breakpoints) în instrumentele pentru dezvoltatori, care opresc execuția codului și oferă informații despre starea curentă a aplicației.
Poți seta puncte de întrerupere în zonele problematice ale codului pentru a analiza erorile în detaliu. Aceasta este una dintre cele mai eficiente metode pentru depanarea aplicațiilor web bazate pe JavaScript.
3. IDE-uri (Integrated Development Environments)
IDE-urile sunt de asemenea foarte utile pentru depanarea codului JavaScript. De exemplu, VS Code permite setarea punctelor de întrerupere. Funcționalitățile de depanare pot varia în funcție de IDE, dar majoritatea suportă această caracteristică.
Integrarea Codului JavaScript într-un Fișier HTML
Codul JavaScript poate fi adăugat într-un fișier HTML folosind eticheta <script>. Iată un exemplu:
<html lang="ro"> <head> <title>exemplu.ro</title> </head> <body> <h2>exemplu.ro</h2> <script> // Codul JavaScript este plasat aici console.log("Acesta este cod JavaScript"); </script> </body> </html>
Ce Sunt Cookie-urile?
Cookie-urile sunt perechi cheie-valoare folosite pentru stocarea unor cantități mici de date. Aceste date pot fi de orice tip. Poți seta o dată de expirare pentru cookie-uri, după care acestea vor fi șterse. Cookie-urile sunt utilizate pe scară largă pentru stocarea informațiilor despre utilizatori.
Cookie-urile nu se șterg când reîmprospătezi pagina, ci doar când sunt șterse în mod specific sau când expiră. Instrumentele de dezvoltare ale browserului îți permit să vizualizezi cookie-urile stocate de orice pagină web.
Cum Citești un Cookie?
Poți citi un cookie în JavaScript folosind `document.cookie`. Această comandă returnează toate cookie-urile create.
console.log("Toate cookie-urile", document.cookie);
Dacă nu există niciun cookie creat, se va returna un șir gol.
Crearea și Ștergerea Cookie-urilor
Poți crea cookie-uri setând perechea cheie-valoare la `document.cookie`. Iată un exemplu:
document.cookie = "unu=Unu;";
În sintaxa de mai sus, „unu” este cheia cookie-ului, iar „Unu” este valoarea acestuia. Poți adăuga mai multe atribute unui cookie, precum domeniu, cale, expirare, separate prin punct și virgulă (;). Toate atributele sunt opționale.
Un exemplu cu atribute:
document.cookie = "unu=Unu;expires=Jan 31 2024;path=/;";
În codul de mai sus, s-a adăugat o dată de expirare și o cale a cookie-ului. Dacă nu se furnizează o dată de expirare, cookie-ul va fi șters la sfârșitul sesiunii. Calea implicită este calea fișierului. Formatul datei de expirare trebuie să fie GMT.
Cum se creează mai multe cookie-uri:
document.cookie = "unu=Unu;expires=Jan 31 2024;path=/;"; document.cookie = "doi=Doi;expires=Jan 31 2024;path=/;"; document.cookie = "trei=Trei;expires=Jan 31 2024;path=/;";
Cookie-urile nu se vor suprascrie dacă cheia sau calea diferă. Dacă cheia și calea sunt aceleași, cookie-ul anterior va fi suprascris. Exemplul de mai jos suprascrie cookie-ul anterior:
document.cookie = "unu=Unu;expires=Jan 31 2024;path=/;"; document.cookie = "unu=Doi;path=/;";
Am eliminat data de expirare din cookie și am schimbat valoarea.
Pentru teste, utilizează o dată de expirare viitoare pentru ca funcționarea să fie corectă. Dacă folosești aceeași dată (31 ianuarie 2024) și după 31 ianuarie 2024, cookie-urile nu vor fi create.
Am văzut cum creăm și actualizăm cookie-uri. Să vedem cum ștergem cookie-uri.
Ștergerea cookie-urilor este simplă: setează data de expirare a cookie-ului la o dată din trecut. Vezi exemplul:
// Crearea cookie-urilor document.cookie = "unu=Unu;expires=Jan 31 2024;path=/;"; document.cookie = "doi=Doi;expires=Jan 31 2024;path=/;"; document.cookie = "trei=Trei;expires=Jan 31 2024;path=/;"; // Ștergerea ultimului cookie document.cookie = "trei=Trei;expires=Jan 1 2024;path=/;";
Nu vei găsi ultimul cookie, deoarece a fost șters în ultima linie de cod. Acesta este un tutorial succint despre cookie-uri.
Framework-uri JavaScript
Există o gamă largă de framework-uri JavaScript disponibile. React, Vue și Angular sunt folosite pentru dezvoltarea interfețelor utilizator (UI). Express, Koa și Nest sunt utilizate pentru dezvoltarea backend-ului. NextJS și Gatsby sunt framework-uri pentru generarea site-urilor statice. React Native și Ionic sunt utilizate pentru dezvoltarea de aplicații mobile. Am menționat câteva framework-uri JavaScript aici, dar explorarea completă a acestora poate dura destul de mult timp. Studiază aceste framework-uri atunci când vei avea nevoie.
Closures în JavaScript
Un closure este o funcție împreună cu domeniul său lexical și mediul lexical părinte. Cu closures, poți accesa datele din domeniul extern. Closures sunt create atunci când sunt definite funcțiile.
function outer() { const a = 1; function inner() { // Aici putem accesa toate datele din domeniul funcției externe // Datele vor fi disponibile chiar dacă executăm această funcție în afara funcției externe // deoarece closure-ul lui inner este creat în timpul definirii acesteia console.log("Accesarea lui a în interiorul lui inner", a); } return inner; } const innerFn = outer(); innerFn();
Closures sunt folosite extensiv în aplicațiile JavaScript. S-ar putea să le fi folosit deja fără să realizezi că sunt closures. Este important să înțelegi în profunzime acest concept.
Hoisting în JavaScript
Hoisting este un proces prin care declarațiile variabilelor, funcțiilor și claselor sunt mutate în partea de sus a domeniului de aplicare înainte de execuția codului.
// Accesarea `name` înainte de declarare console.log(name); // Declararea și inițializarea lui `name` var name = "exemplu.ro";
Dacă rulezi codul de mai sus, nu vei vedea nicio eroare. Dar în majoritatea limbajelor, vei primi o eroare. Ieșirea va fi nedefinită, deoarece hoisting mută doar declarațiile în partea de sus și nu o va inițializa până la linia numărul 3.
Schimbă `var` în `let` sau `const` și rulează codul din nou:
// Accesarea `name` înainte de declarare console.log(name); // Declararea și inițializarea lui `name` const name = "exemplu.ro";
Acum, vei obține o eroare de referință, care spune că nu poți accesa variabila înainte de inițializare.
ReferenceError: Nu se poate accesa 'name' înainte de inițializare
Deci, `let` și `const` au fost introduse în ES6 și nu pot fi accesate înainte de inițializare, după cum sugerează eroarea. Aceasta se datorează faptului că variabilele declarate cu `let` sau `const` se află în Zona Moartă Temporală (TDZ) până la inițializarea liniei. Nu se pot accesa variabilele din TDZ.
Currying în JavaScript
Currying este o tehnică de transformare a unei funcții cu mai mulți parametri într-o serie de funcții cu un singur parametru. Cu aceasta, putem converti o funcție de genul `add(a, b, c, d)` într-o funcție de genul `add(a)(b)(c)(d)`. Vezi exemplul:
function getCurryCallback(callback) { return function (a) { return function (b) { return function (c) { return function (d) { return callback(a, b, c, d); }; }; }; }; } function add(a, b, c, d) { return a + b + c + d; } const curriedAdd = getCurryCallback(add); // Apelarea lui curriedAdd console.log(curriedAdd(1)(2)(3)(4));
Putem generaliza funcția `getCurryCallback`, care poate fi folosită pentru diverse funcții pentru a le transforma în funcții curry. Poți căuta mai multe informații despre curry în documentația JavaScript.
Diferența Dintre Document și Fereastră
Fereastra este obiectul cel mai de sus în browser. Acesta conține toate informațiile despre fereastra browserului, cum ar fi istoricul, locația, navigatorul și este disponibil la nivel global în JavaScript. Îl putem folosi direct în codul nostru, fără importuri. Putem accesa proprietățile și metodele obiectului fereastră fără a folosi direct `window`.
Documentul este o parte a obiectului fereastră. Întreg codul HTML încărcat în pagină este convertit în obiectul document. Obiectul document se referă la elementul special HTMLDocument, care va avea proprietăți și metode diferite, ca toate elementele HTML.
Obiectul `window` reprezintă fereastra browserului, iar `document` reprezintă documentul HTML încărcat în fereastra respectivă.
Diferența Dintre Partea Client și Partea Server
Partea client se referă la utilizatorul final care utilizează aplicația, iar partea server se referă la serverul web unde este implementată aplicația.
În terminologia frontend, putem considera browserul utilizatorului ca parte client și serviciile cloud ca parte server.
Diferența Dintre innerHTML și innerText
Atât `innerHTML`, cât și `innerText` sunt proprietăți ale elementelor HTML. Poți schimba conținutul unui element HTML folosind aceste proprietăți.
Când atribui un șir HTML la proprietatea `innerHTML`, aceasta va fi redată ca HTML. Vezi exemplul:
const titleEl = document.getElementById("title"); titleEl.innerHTML = '<span style="color:orange;">exemplu.ro</span>';
Adaugă un element cu ID-ul „title” în HTML și adaugă codul JavaScript de mai sus. Vei vedea că „exemplu.ro” apare cu culoarea portocalie și este în interiorul etichetei `span`. Deci, `innerHTML` va lua șirul HTML și îl va interpreta ca HTML.
`innerText`, pe de altă parte, va lua un șir normal și îl va reda exact așa cum este. Nu va interpreta niciun element HTML. Schimbă `innerHTML` cu `innerText` în codul de mai sus și vezi rezultatul:
const titleEl = document.getElementById("title"); titleEl.innerText = '<span style="color:orange;">exemplu.ro</span>';
Acum, vei vedea șirul exact pe care l-ai furnizat în pagina web.
Diferența Dintre let și var
Cuvintele cheie `let` și `var` sunt folosite pentru a crea variabile în JavaScript. Cuvântul cheie `let` a fost introdus în ES6.
`let` are un domeniu de aplicare la nivel de bloc, iar `var` are un domeniu de aplicare la nivel de funcție.
{ let a = 2; console.log("În interiorul blocului", a); } console.log("În afara blocului", a);
Rulează codul de mai sus. Vei obține o eroare pe ultima linie, deoarece nu poți accesa `a` în afara blocului. Acum, schimbă `let` în `var` și rulează-l din nou:
{ var a = 2; console.log("În interiorul blocului", a); } console.log("În afara blocului", a);
Nu vei obține nicio eroare, deoarece poți accesa variabila și în afara blocului. Acum, înlocuiește blocul cu o funcție:
function sample() { var a = 2; console.log("În interiorul funcției", a); } sample(); console.log("În afara funcției", a);
Vei obține o eroare de referință dacă rulezi codul de mai sus, deoarece nu poți accesa `var a` în afara funcției.
Poți redeclara variabile folosind cuvântul cheie `var`, dar nu poți redeclara variabilele folosind `let`. Iată un exemplu:
var a = "exemplu.ro"; var a = "Chandan"; console.log(a);
let a = "exemplu.ro"; let a = "Chandan"; console.log(a);
Primul fragment de cod nu va genera nicio eroare, iar valoarea lui `a` va fi schimbată la cea mai recentă valoare atribuită. Al doilea fragment de cod va genera o eroare, deoarece nu se pot redeclara variabile folosind `let`.
Diferența Dintre Stocarea de Sesiune și Stocarea Locală
Stocarea de sesiune și stocarea locală sunt folosite pentru a stoca informații pe computerele utilizatorilor, care pot fi accesate fără internet. Se pot stoca perechi cheie-valoare atât în stocarea de sesiune, cât și în stocarea locală. Atât cheia, cât și valoarea vor fi convertite în șiruri de caractere dacă furnizezi orice alt tip de date sau structură de date.
Stocarea de sesiune se șterge după ce se încheie sesiunea (când browserul este închis). Stocarea locală nu se șterge decât atunci când este ștearsă explicit.
Poți accesa, actualiza și șterge stocarea de sesiune și stocarea locală cu obiectele `sessionStorage`, respectiv `localStorage`.
Ce Este NaN în JavaScript?
NaN înseamnă „Not-a-Number” (Nu este un Număr). Acesta indică faptul că o valoare nu este un număr valid în JavaScript. Sunt situații în care rezultatul va fi NaN, cum ar fi 0/0, nedefinit * 2, 1 + nedefinit, null * nedefinit etc.
Ce Este Lexical Scoping?
Sfera lexicală se referă la accesarea variabilelor din domeniul părinților săi. Să presupunem că avem o funcție cu două funcții interioare. Funcția cea mai interioară poate accesa variabilele de aplicare ale celor două funcții părinte. În mod similar, funcția de al doilea nivel poate accesa domeniul de aplicare al funcției celei mai externe. Vezi exemplul:
function outermost() { let a = 1; console.log(a); function middle() { let b = 2; // `a` este accesibil aici console.log(a, b); function innermost() { let c = 3; // atât `a`, cât și `b` sunt accesibile aici console.log(a, b, c); } innermost(); } middle(); } outermost();
JavaScript folosește un lanț de domenii pentru a găsi variabila atunci când o accesezi undeva în cod. În primul rând, se va verifica variabila din domeniul curent, apoi domeniul părinte până la domeniul global.
Ce Înseamnă Trecere Prin Valoare și Trecere Prin Referință?
Trecerea prin valoare și trecerea prin referință sunt două modalități de transmitere a argumentelor unei funcții în JavaScript.
Trecere prin valoare: creează o copie a datelor originale și o transmite funcției. Astfel, modificările făcute în funcție nu vor afecta datele originale. Iată un exemplu:
function sample(a) { // schimbarea valorii lui `a` a = 5; console.log("În interiorul funcției", a); } let a = 3; sample(a); console.log("În afara funcției", a);
Vei vedea că valoarea inițială a lui `a` nu este modificată, chiar dacă am modificat-o în interiorul funcției.
Trecere prin referință: transmite referința datelor către funcție. Astfel, modificările făcute în funcție vor schimba și datele originale:
function sample(arr) { // adăugarea unei noi valori la matrice arr.push(3); console.log("În interiorul funcției", arr); } let arr = [1, 2]; sample(arr); console.log("În afara funcției", arr);
Vei vedea că valoarea inițială a lui `arr` este modificată atunci când o schimbăm în interiorul funcției.
Notă: toate tipurile de date primitive sunt transmise prin valoare, iar cele neprimitive sunt transmise prin referință.
Ce Este Memorizarea?
Memorizarea este o tehnică care stochează valorile calculate într-o cache și le folosește din nou când avem nevoie de ele, fără a le recalcula. Aceasta accelerează execuția codului în cazul calculelor dificile, existând un compromis de stocare, care nu este un impediment major în comparație cu timpul câștigat.
const memo = {}; function add(a, b) { const key = `${a}-${b}`; // verificarea dacă am calculat deja valoarea if (memo[key]) { console.log("Nu se mai calculează"); return memo[key]; } // adăugarea noii valori calculate în cache // cache-ul este un simplu obiect global memo[key] = a + b; return memo[key]; } console.log(add(1, 2)); console.log(add(2, 3)); console.log(add(1, 2));
Acesta este un exemplu simplu care demonstrează memorizarea. Adunarea a două numere nu este un calcul greu, acest exemplu este doar pentru demonstrație.
Ce Este Parametrul Rest?
Parametrul rest este folosit pentru a colecta toți parametrii rămași ai unei funcții. Să presupunem că avem o funcție care va accepta minim 2 argumente și poate accepta orice număr de parametri la maximum. Deoarece nu avem un număr maxim de argumente, putem colecta primii 2 parametri cu variabile normale și restul parametrilor cu parametrul rest folosind operatorul rest.
function sample(a, b, ...rest) { console.log("Parametrul rest", rest); } sample(1, 2, 3, 4, 5);
Parametrul rest va fi o matrice a ultimelor trei argumente din exemplul de mai sus. Astfel, poți avea orice număr de parametri pentru o funcție.
O funcție poate avea un singur parametru rest, care trebuie să fie ultimul în ordinea parametrilor.
Ce Este Destructurarea Obiectelor?
Destructurarea obiectelor este utilizată pentru a accesa variabilele din obiect și a le atribui variabilelor cu aceleași nume ca și cheile obiectului. Iată un exemplu:
const object = { a: 1, b: 2, c: 3 }; // Destructurarea obiectului const { a, b, c } = object; // Acum, a, b, c vor fi folosite ca variabile normale console.log(a, b, c);
Poți schimba numele variabilelor destructurate, după cum urmează:
const object = { a: 1, b: 2, c: 3 }; // Schimbarea numelor lui `a` și `b` const { a: changedA, b: changedB, c } = object; // Acum, changedA, changedB, c vor fi folosite ca variabile normale console.log(changedA, changedB, c);
Ce Este Destructurarea Matricei?
Destructurarea matricei este folosită pentru a accesa variabilele dintr-o matrice și pentru a le atribui variabilelor. Vezi exemplul:
const array = [1, 2, 3]; // Destructurarea matricei // Se bazează pe indexul matricei const [a, b, c] = array; // Acum, poți folosi a, b, c ca variabile normale console.log(a, b, c);
Capturarea Evenimentelor și Propagarea Evenimentelor
Capturarea evenimentelor și propagarea evenimentelor sunt două moduri de propagare a evenimentelor în HTML DOM. Să presupunem că există două elemente HTML, unul în interiorul celuilalt, și un eveniment are loc pe elementul interior. Modul de propagare a evenimentelor va decide ordinea execuției acestor evenimente.
Propagarea evenimentelor (Event bubbling): rulează mai întâi handlerul de evenimente pe element, apoi elementul său părinte, și tot așa, până la elementul cel mai de sus. Acesta este comportamentul implicit al tuturor evenimentelor.
Capturarea evenimentelor (Event capturing): trebuie specificat cazul în care vrei să folosești acest tip de propagare a evenimentelor. Se poate specifica în timp ce adaugi un ascultător de evenimente. Evenimentele se vor executa în următoarea ordine, dacă avem activată capturarea evenimentelor:
- Evenimentele încep să se execute de la elementul cel mai de sus până la elementul țintă.
- Evenimentul de pe elementul țintă va fi executat din nou.
- Propagarea evenimentului (bubbling) va avea loc din nou până când se ajunge la elementul cel mai de sus.
Propagarea evenimentului poate fi oprită apelând `event.stopPropogation()` în cadrul handlerului de evenimente.
Ce Sunt Promisiunile în JavaScript?
Obiectul Promise este folosit pentru operațiunile asincrone care se vor finaliza în viitor cu o stare de succes sau eșec.
O promisiune se poate afla în una dintre următoarele stări:
- În așteptare (pending) – când operațiunea este în curs de desfășurare.
- Îndeplinită (fulfilled) – când operațiunea s-a finalizat cu succes. Vom avea rezultate (dacă există) în starea de succes.
- Respinsă (rejected) – când operațiunea s-a finalizat cu o eroare. Vom avea motivul (eroarea) pentru care nu a reușit.
Iată două exemple de cazuri de succes și