JavaScript este limba oficială a toate browserele web moderne. Ca atare, întrebările JavaScript apar în tot felul de interviuri cu dezvoltatorii.

Acest articol nu se referă la cele mai noi biblioteci JavaScript, practici de dezvoltare obișnuite sau la oricare dintre noile Funcții ES6. Mai degrabă, este vorba despre 3 lucruri care apar de obicei în interviuri atunci când se discută JavaScript. Eu însumi mi s-au pus aceste întrebări, iar prietenii mei mi-au spus că și ei li s-au pus.

Desigur, acestea nu sunt singurele 3 lucruri pe care ar trebui să le studiați înainte de un interviu JavaScript – există un multitudine de căi tu poate sa pregătiți-vă mai bine pentru un interviu viitoare – dar mai jos sunt 3 întrebări pe care le poate pune un intervievator pentru a judeca cât de bine cunoașteți și înțelegeți limbajul JavaScript și DOM.

Deci sa începem! Rețineți că vom folosi JavaScript vaniliat în exemplele de mai jos, deoarece intervievatorul dvs. va dori, de obicei, să vadă cât de bine înțelegeți JavaScript și DOM fără ajutorul unor biblioteci precum jQuery.

Întrebarea nr. 1: delegarea evenimentului

Când creați o aplicație, uneori va trebui să atașați ascultători de evenimente la butoane, text sau imagini de pe pagină pentru a efectua o acțiune când utilizatorul interacționează cu elementul.

Dacă luăm ca exemplu o listă simplă de lucruri, intervievatorul vă poate spune că dorește să aibă loc o acțiune atunci când un utilizator face clic pe unul dintre elementele listei. Și vor să implementați această funcționalitate în JavaScript, presupunând următorul cod HTML:

<ul id="todo-app">
  <li class="item">Walk the dog</li>
  <li class="item">Pay bills</li>
  <li class="item">Make dinner</li>
  <li class="item">Code for one hour</li>
</ul>

Poate doriți să faceți ceva de genul următor pentru a atașa ascultători de evenimente la elemente:

document.addEventListener('DOMContentLoaded', function() {
  
  let app = document.getElementById('todo-app');
  let items = app.getElementsByClassName('item');
  
  // attach event listener to each item
  for (let item of items) {
    item.addEventListener('click', function() {
      alert('you clicked on item: ' + item.innerHTML);
    });
  }
  
});

În timp ce acest lucru funcționează tehnic, problema este că atașați un ascultător de evenimente la fiecare articol individual. Este bine pentru 4 elemente, dar ce se întâmplă dacă cineva adaugă 10.000 de articole (poate avea multe lucruri de făcut) la lista lor de lucruri? Apoi, funcția dvs. va crea 10.000 de ascultători de evenimente separate și le va atașa pe fiecare la DOM. Nu este foarte eficient.

Într-un interviu, cel mai bine ar fi să întrebați mai întâi intervievatorul care este numărul maxim de elemente pe care utilizatorul îl poate introduce. Dacă nu poate fi niciodată mai mare de 10, de exemplu, atunci codul de mai sus ar funcționa bine. Dar dacă nu există o limită a numărului de articole pe care utilizatorul le poate introduce, atunci ați dori să utilizați o soluție mai eficientă.

Dacă aplicația dvs. ar putea ajunge la sute de ascultători de evenimente, soluția mai eficientă ar fi să atașați efectiv unu ascultător de evenimente la întregul container și apoi să puteți accesa fiecare articol atunci când este efectiv făcut clic. Aceasta se numește delegarea evenimentuluiși este mult mai eficient decât atașarea handlerelor de evenimente separate.

Iată codul pentru delegarea evenimentului:

document.addEventListener('DOMContentLoaded', function() {
  
  let app = document.getElementById('todo-app');
  
  // attach event listener to whole container
  app.addEventListener('click', function(e) {
    if (e.target && e.target.nodeName === 'LI') {
      let item = e.target;
      alert('you clicked on item: ' + item.innerHTML);
    }
  });
  
});

Întrebarea nr. 2: utilizarea unei închideri într-o buclă

Închiderile sunt uneori prezentate într-un interviu, astfel încât intervievatorul să poată evalua cât de familiarizați cu limba și dacă știți când să implementați o închidere.

O închidere este practic atunci când un funcția interioară are acces la variabile în afara domeniului său de aplicare. Închiderile pot fi utilizate pentru lucruri precum implementarea confidențialității și crearea fabrici de funcții. O întrebare comună la interviu cu privire la utilizarea dispozitivelor de închidere este ceva de genul acesta:

Scrieți o funcție care va parcurge o listă de numere întregi și va imprima indexul fiecărui element după o întârziere de 3 secunde.

O implementare obișnuită (incorectă) pe care am văzut-o pentru această problemă arată cam așa:

const arr = [10, 12, 15, 21];
for (var i = 0; i < arr.length; i++) {
  setTimeout(function() {
    console.log('The index of this number is: ' + i);
  }, 3000);
}

Dacă rulați acest lucru, veți vedea că de fapt obțineți 4 tipărite de fiecare dată în loc de cele așteptate 0, 1, 2, 3 după o întârziere de 3 secunde.

Pentru a identifica corect de ce se întâmplă acest lucru, ar fi util să înțelegem de ce se întâmplă acest lucru în JavaScript, exact ceea ce încearcă să testeze intervievatorul.

Motivul pentru aceasta este că setTimeout funcția creează o funcție (închiderea) care are acces la scopul său exterior, care este bucla care conține indexul i. După trecerea a 3 secunde, funcția este executată și imprimă valoarea lui i, care la sfârșitul buclei este la 4 deoarece ciclează prin 0, 1, 2, 3, 4 și bucla se oprește în cele din urmă la 4.

Există de fapt câteva moduri de scriind corect funcția pentru această problemă. Iată două dintre ele:

const arr = [10, 12, 15, 21];
for (var i = 0; i < arr.length; i++) {
  // pass in the variable i so that each function 
  // has access to the correct index
  setTimeout(function(i_local) {
    return function() {
      console.log('The index of this number is: ' + i_local);
    }
  }(i), 3000);
}
const arr = [10, 12, 15, 21];
for (let i = 0; i < arr.length; i++) {
  // using the ES6 let syntax, it creates a new binding
  // every single time the function is called
  // read more here: http://exploringjs.com/es6/ch_variables.html#sec_let-const-loop-heads
  setTimeout(function() {
    console.log('The index of this number is: ' + i);
  }, 3000);
}

Întrebarea # 3: Debouncing

Există unele evenimente din browser care se pot declanșa de mai multe ori într-o perioadă scurtă de timp foarte rapid, cum ar fi redimensionarea unei ferestre sau derularea unei pagini. Dacă atașați un ascultător de evenimente la evenimentul de derulare a ferestrei, de exemplu, și utilizatorul derulează continuu pagina în jos foarte repede, evenimentul dvs. se poate declanșa de mii de ori în interval de 3 secunde. Acest lucru poate cauza unele probleme grave de performanță.

Dacă discutați despre crearea unei aplicații într-un interviu și evenimente cum ar fi derularea, redimensionarea ferestrei sau apăsarea tastei, asigurați-vă că menționați reducerea și / sau limitarea ca o modalitate de a îmbunătăți viteza și performanța paginii. Un exemplu real luat din aceasta postare de invitat pe css-trucuri:

În 2011, a apărut o problemă pe site-ul Twitter: când derulați fluxul dvs. Twitter, acesta a devenit lent și nu răspunde. Publicat de John Resig o postare pe blog despre problemă unde s-a explicat cât de proastă este ideea de a atașa direct funcții costisitoare la scroll eveniment.

Debouncing este o modalitate de a rezolva această problemă prin limitarea timpului care trebuie să treacă până când o funcție este apelată din nou. Prin urmare, o implementare corectă a debouncing-ului ar fi grup mai multe funcții apelează într-una și o execută o singură dată după ce a trecut un timp. Iată o implementare în JavaScript simplu care folosește subiecte precum scop, închideri, acest, și sincronizarea evenimentelor:

// debounce function that will wrap our event
function debounce(fn, delay) {
  // maintain a timer
  let timer = null;
  // closure function that has access to timer
  return function() {
    // get the scope and parameters of the function 
    // via 'this' and 'arguments'
    let context = this;
    let args = arguments;
    // if event is called, clear the timer and start over
    clearTimeout(timer);
    timer = setTimeout(function() {
      fn.apply(context, args);
    }, delay);
  }
}

Această funcție – atunci când este înfășurată în jurul unui eveniment – se va executa numai după ce a trecut un anumit timp.

Ați folosi această funcție așa:

// function to be called when user scrolls
function foo() {
  console.log('You are scrolling!');
}

// wrap our function in a debounce to fire once 2 seconds have gone by
let elem = document.getElementById('container');
elem.addEventListener('scroll', debounce(foo, 2000));

Limitarea este o altă tehnică, care este similară cu debouncing-ul, cu excepția faptului că, în loc să aștepte să treacă ceva timp înainte de a apela o funcție, limitarea răspândește doar apelurile de funcții pe un interval de timp mai lung. Deci, dacă un eveniment are loc de 10 ori în decurs de 100 de milisecunde, limitarea ar putea răspândi fiecare dintre apelurile de funcții care urmează să fie executate o dată la 2 secunde în loc să se declanșeze toate în 100 de milisecunde.

Pentru mai multe informații despre debouncing și limitare, următoarele articole și tutoriale pot fi utile:

Dacă v-a plăcut să citiți acest articol, atunci vă poate dori să citiți tutorialele JavaScript și să rezolvați unele dintre provocările de codare JavaScript pe care le găzduiesc Coderbyte. Mi-ar plăcea să aud ce crezi!