Jedną z części rozmowy rekrutacyjnej na stanowisko Junior Web Developera jest rozmowa techniczna. Często podczas tej części rozmowy rekruter poprosi Cię o opisanie projektów, w których do tej pory brałeś(aś) udział. Warto wtedy opisać czego się nauczyłeś(aś), jakie trudności napotkałeś(aś) oraz jak udało Ci się z nimi uporać. W wielu przypadkach rekruterowi to wystarczy. Gdy jednak nie masz zbyt wiele doświadczenia, to rekruter będzie chciał sprawdzić Twoją wiedzę poprzez serię kilku pytań technicznych.
Część pytań pochodzi z portalu DevFAQ — bazy z pytaniami rekrutacyjnymi tworzonej przez społeczność. Część pytań pochodzi z mojego e-booka 106 Pytań Rekrutacyjnych Junior JavaScript Developer, który możesz odebrać, zapisując się na mój mailing. Nie wszystkie pytania zawarte w tym artykule znajdziesz w moim e-booku, więc zachęcam Cię do przeczytania tego wpisu, nawet jeśli planujesz przeczytać e-booka.
Na blogu tematyce pytań technicznych poświęciłem szerszą uwagę w serii wpisów. Zachęcam do sprawdzenia pozostałych artykułów:
- Junior Web Developer – pytania rekrutacyjne cz. 1
- Junior Web Developer – pytania rekrutacyjne cz. 2
- Junior Web Developer – pytania rekrutacyjne cz. 3
- Junior Web Developer – pytania rekrutacyjne cz. 5
- Junior Web Developer – pytania rekrutacyjne – React
- Programista – pytania rekrutacyjne – TypeScript
- Programista – pytania rekrutacyjne – Git
- Programista – pytania rekrutacyjne – Docker
- Programista – pytania rekrutacyjne – bazy danych
Pytania z tej serii możesz również traktować jako trening i sprawdzenie swoich umiejętności przed rozmową rekrutacyjną.
Jakie znasz biblioteki wykorzystywane w testach automatycznych?
Dla potrzeb testów automatycznych w JavaScript powstało wiele rozwiązań pozwalających pisać dobre testy automatyczne:
- Mocha — jeden z najpopularniejszych frameworków do testów jednostkowych w JavaScript. Dobrze sprawdza się z biblioteką Chai, która jest wykorzystywana do projektowania asercji w testach.
- Jest — biblioteka do testów automatycznych stworzona przez Meta (Facebook). Jest domyślną biblioteką do testów w Nest.js, a także często można go znaleźć w projektach Reactowych (np. jest domyślną biblioteką w Create React App).
- Jasmine — twórcy określają tę bibliotekę jako behavior-driven development framework. Alternatywne rozwiązanie dla Jest czy Mocha.
- Karma — pomaga w automatyzacji testów jednostkowych w środowisku JavaScript. Służy do uruchamiania testów jednostkowych na różnych przeglądarkach internetowych i różnych platformach.
- AVA — framework do testów jednostkowych, który jest znany z równoległego wykonywania testów.
- Cypress — framework do testowania end-to-end, który umożliwia testowanie całych aplikacji, w tym interakcji użytkownika.
- Nyc — biblioteka dostarczająca metrykę pokrycia kodu testami (code coverage)
- Sinon — biblioteka dostarczająca możliwość tworzenia „obiektów-wydmuszek”, śledzenia wywołań poszczególnych metod czy manipulowaniu czasem systemowym w testach.
Rozwiązań do tworzenia testów automatycznych jest naprawdę sporo. Jeśli korzystasz z czegoś spoza przedstawionej listy, to również nie zapomnij o nich wspomnieć. Chętnie poznam nowe narzędzia, więc zachęcam do podzielenia się nim w komentarzu.
Jak spowodować, aby kliknięcie etykiety checkboxa powodowało jego zaznaczenie?
Teoretycznie można do tego wykorzystać JavaScript. Jednak znacznie prościej można to zrobić poprzez nadanie atrybutu id
na znacznik input
z typem checkbox
oraz nadanie atrybutu for
dla znacznika label
. Wartość atrybutu for powinna być identyczna z id
docelowego checkboxa. Analogicznie działa to dla inputów typu radio.
<label for="agreement">I agree</label>
<input type="checkbox" id="agreement">
Czym się różnią w CSS style display
: inline
, inline-block
i block
?
Elementy wyświetlane jako inline
są wyświetlane obok siebie w jednej linii tekstu. Szerokość i wysokość są dostosowywane do zawartości znacznika. Przykładem znaczników z domyślnym display ustawionym na inline
są znaczniki span
lub a
. Ustawiając dwa takie znaczniki obok siebie, ich zawartość będzie się znajdować jeden za drugim. Ponadto w przypadku inline
nie ma możliwości ustawienia właściwości width
i height
.
Inaczej sytuacja będzie wyglądać np. przypadku znacznika p
, który domyślnie jest znacznikiem typu block
, czyli nawet. Jeśli jego zawartość składa się tylko z jednego znaku, to i tak zajmie całą dostępną szerokość rodzica (kontenera).
Właściwość inline-block
jest kompromisem między tymi rozwiązaniami. Dostajemy tu dostęp do właściwości width
, height
, ale elementy wciąż wyświetlą się obok siebie tak, jak w stylu inline
. Elementy inline-block
tworzą nową linię wtedy, gdy przekroczy się dostępną szerokość kontenera. W przeciwnym razie zachowują się jak inline
. Element zajmie tylko tyle szerokości, ile potrzebuje.
Do przetestowania opisanych zachowań możesz wykorzystać poniższy fragment kodu. Skopiuj go i sprawdź, jak zmienia się wygląd znaczników przy zmianie wartości display
.
div {
<style>
div {
display: inline;
padding: 100px;
background-color: red;
margin: 10px;
width: 50px;
height: 50px;
}
</style>
<div>foo</div>
<div>bar</div>
Czym są encje w HTML?
Jeśli potrzebujemy wstawić w tekst na stronie WWW jakiś znak specjalny lub znak stanowiący element składni HTML, to możemy skorzystać z encji. Encję tworzy się poprzez znak &
, następnie nazwa encji zakończona średnikiem. Na przykład encja znaku copyright (©
) to ©, a encje dla znaków < oraz > są to odpowiednio <
i >
.
Do wykonania zadanie polegające na wysłaniu Ajaxem obiektu i zapisaniu w Local Storage tokena, który dostanie się w ramach odpowiedzi. W przypadku błędu wyświetlić komunikat o błędzie.
Z tego typu zadaniami spotkasz się w swojej codziennej pracy. Do wykonania tego zadania możesz wykorzystać Fetch API lub dowolnego innego klienta HTTP. Wykorzystując Fetch API, nie jest konieczna instalacja dodatkowych zależności. W proponowanym rozwiązaniu metoda fetch()
przyjmie dwa parametry: ścieżkę do zasobu oraz obiekt, w którym zdefiniowana zostanie metoda HTTP i obiekt z danymi, który otrzymamy w zadaniu.
fetch( 'https://example.com/token', {
method: 'POST',
body: JSON.stringify(obj)
} )
.then( response => response.json() )
.then( response => {
localStorage.setItem( 'token', response.data.token );
} )
.catch( error => console.error( 'An error occured:', error ) );
Czym jest pure function?
Aby funkcję można było nazwać pure, musi ona spełniać dwa wymagania:
- dla takiego samego zestawu danych wejściowych zawsze musi zwracać taki sam wynik,
- pure function nie może modyfikować wartości (np. zmiennych poza swoim zakresem czy danych wejściowych).
Poniżej znajdziesz przykłady funkcji pure i impure:
let a = 2;
// Pure function
function add( a, b ) {
return a + b;
}
// Impure function - modyfikuje zmienną spoza swojego zakresu
function increment() {
return a++;
}
// Impure function - dla takich samych danych wejściowych
// wynik za każdym razem będzie inny
function random( a ) {
return Math.random() * a;
}
console.log( add( 2, 4 ) );
increment();
console.log( a );
console.log( random( 3 ) );
Więcej o temacie pure functions możesz dowiedzieć się z mojego artykułu, gdzie w szczegółach omówiłem, kiedy się przydają i dlaczego warto się tym zagadnieniem zainteresować.
Czym są atrybuty data
i jak można je wykorzystać?
Atrybuty data
można dodawać do znaczników HTML, aby przypisywać do nich dodatkowe informacje. Atrybuty data dodaje się w postaci data-[nazwa_atrybutu]
. Przykładem wykorzystania może być przypadek, gdy chcemy wyświetlić na stronie WWW rekordy z bazy danych. Każdy rekord można wyświetlić w osobnym znaczniku div
, a do znacznika dodać atrybut data-id
wraz z identyfikatorem tego rekordu. Atrybut data-id
można następnie wykorzystać w kodzie CSS lub JavaScript.
<div class="record" data-id="54759eb3c090d83494e2d804">foo</div>
<script>
const record = document.querySelector('.record');
console.log( record.dataset.id )// "54759eb3c090d83494e2d804"
</script>
<style>
.record[data-id='54759eb3c090d83494e2d804'] {
color: red;
}
</style>
Czym się różni function declaration od function expression?
Function declaration to sposób definiowania funkcji za pomocą słowa kluczowego function
, nazwy funkcji i bloku kodu. Function declaration zostają załadowane, zanim kod zostanie wykonany. Natomiast function expression to function declaration przypisane do zmiennej. Powoduje to, że function expression zostaje załadowane dopiero w momencie, gdy interpreter napotka je w kodzie.
Wywołanie function expression przed jego faktycznym wystąpieniem w kodzie będzie skutkować błędem, ponieważ albo zmienna uległa hoistingowi, lub też w przypadku let
i const
taka zmienna znajduje się w TDZ. Jeśli pojęcia hoistingu i TDZ są Ci obce, to sprawdź poprzedni artykuł z tej serii.
console.log( add( 2, 4 ) );
console.log( random( 3 ) );
// Function expression
var add = function( a, b ) {
return a + b;
}
// Function declaration
function random( a ) {
return Math.random() * a;
}
Uruchomienie powyższego fragmentu kodu spowoduje błąd. Przenosząc wywołanie funkcji pod function expression, kod zacznie działać poprawnie.
Czym się różni atrybut od właściwości (property) węzła HTML?
Tworząc szablon HTML strony i dodając znaczniki, nadajemy im atrybuty. Na przykład, w elemencie a
atrybut href
zawiera adres URL odnośnika. Kod HTML jest ładowany przez przeglądarkę i na jego podstawie tworzone jest drzewo DOM.
Każdy element drzewa DOM posiada natomiast już właściwości. Właściwości to informacje dostępne za pośrednictwem obiektu reprezentującego dany element HTML w drzewie DOM. Są to cechy, które można odczytywać i zmieniać za pomocą JavaScript. Za pomocą metod takich jak getElementById
czy querySelector
wywołanych na elemencie document
można uzyskać dostęp do właściwości elementu.
Można podnieść argument, że to kwestia nazewnictwa i czepianie się o szczegóły, które ostatecznie nie mają dużego znaczenia. Uważam jednak, że warto znać poprawne nazewnictwo, by podczas dyskusji z innymi programistami wiedzieć, że dyskutujemy o tym samym zagadnieniu.
Wyjaśnij, jakie elementy zostaną zawarte w następujących selektorach CSS: div, p
; div p
; div > p
; div + p
; div ~ p
.
div, p
— style zostaną zaaplikowane na znacznikidiv
oraz na znacznikip
.div p
— wybiera wszystkie elementy<p>
, które są potomkami elementu<div>
.div > p
— obejmie wszystkie elementyp
, których bezpośrednim rodzicem jest znacznikdiv
. Elementy<p>
, które są wnukami lub bardziej odległymi potomkami elementu<div>
, nie zostaną uwzględnione.div + p
— wybiera pierwszy element<p>
, który występuje zaraz po elemencie<div>
.div ~ p
— wybiera wszystkie elementy<p>
, które są na tym samym poziomie zagnieżdżenia co element<div>
i występują po nim.
Możesz wykorzystać poniższy fragment kodu, by przetestować zachowanie poszczególnych selektorów.
<style>
div p {
color: red;
}
</style>
<div>
<p>First</p>
<p>Second</p>
<section>
<p>Third</p>
</section>
</div>
<p>Fouth</p>
<p>Fifth</p>
Czym są Local Storage, Session Storage oraz cookies?
Te trzy mechanizmy pozwalają na przechowywanie niewielkich porcji danych w przeglądarce w celu ich późniejszego użycia. Do takich danych można zaliczyć wszelkiego rodzaju identyfikatory, tokeny, konfiguracje itp.
Podstawową różnicą między Local Storage, Session Storage a cookies jest to, że cookies możemy odczytać z poziomu serwera. Zawartość Local Storage i Session Storage można odczytać tylko z poziomu przeglądarki.
Kolejną różnicą jest to, że cookies mają ograniczony czas życia. Można ustawić, aby czas życia ciasteczka wynosił np. 1 dzień lub 1 godzinę. Inaczej sprawa wygląda przy Local Storage i Session Storage. W przypadku pierwszego z nich, dane można usunąć poprzez wykorzystanie Web Storage API lub wyczyszczenie danych ręcznie. Session Storage traci dane po zakończeniu sesji strony.
Różnica jest też w maksymalnych rozmiarach. Cookies oferują maksymalnie 4 KB (niektóre przeglądarki oferują więcej), Local Storage 5 MB (wg specyfikacji HTML5) z możliwością rozszerzenia, a rozmiar Session Storage limituje rozmiar pamięci podręcznej.
Jakie znasz rodzaje pętli?
for
– pętla występująca w większości języków programowania. Jej zasada jest oparta na stworzeniu iteratora, zdefiniowaniu warunku zakończenia pętli oraz modyfikacji iteratora podczas każdej iteracji;while
– kolejna klasyczna pętla. Pętla ta wykonuje kolejne iteracje, dopóki warunek w niej zawarty jest prawdziwy;do…while
– zasada działania tej pętli jest identyczna z działaniem pętliwhile
. Tym, co ją odróżnia, jest to, że ta pętla najpierw wykonuje kod, a dopiero potem sprawdza warunek;for…in
– pętla ta pozwala nam na iterowanie po właściwościach obiektu, które są enumerable;for…of
– dzięki tej pętli można iterować po wartościach w typach danych, które umożliwiają iterowanie (np. tablice, stringi, obiekty typuMap
czySet
).
Podaj sposoby, jakimi optymalizował(a)byś wydajność strony WWW.
Poniżej przedstawiam kilka propozycji odpowiedzi na to pytanie:
- kompresja zdjęć, plików audio i video zamieszczanych na stronie;,
- lazy loading dla zdjęć i video;
- korzystanie z CDN-ów;
- optymalizacja na poziomie kodu – usunięcie zbędnego i nieużywanego kodu, refaktoryzacja i optymalizacja używanego kodu;
- minifikacja i obfuskacja kodu;
- włączenie kompresji Gzip/Brotli;
- usunięcie blokującego JavaScript;
- umiejętne cache-owanie zasobów;
- redukcja liczby zapytań HTTP.
Samych technik optymalizacji wydajności jest znacznie więcej. Gorąco zachęcam do podzielenia się w komentarzu, z jakich technik optymalizacji wydajności stron korzystasz. Zachęcam też do zapoznania się z artykułem na blogu piecioshka.pl, gdzie znajdziesz o wiele więcej technik optymalizacji.
Na czym polega lazy loading?
Lazy loading jest techniką optymalizacji ładowania zasobów na stronie internetowej. Polega ona na opóźnianiu ładowania pewnych elementów strony do momentu, gdy są one faktycznie potrzebne. Celem lazy loading jest przyspieszenie ładowania strony i zmniejszenie zużycia przepustowości sieci. Efekt będzie widoczny szczególnie w przypadku stron z dużą ilością treści multimedialnej. Technikę lazy loading najczęściej wykorzystuje się dla obrazów, ale można ją też wykorzystać do opóźnienia ładowania kodu JS, CSS, fontów i elementów iframe
.
Napisz funkcję, która będzie wywoływać się co 0,5 sekundy. Funkcja powinna wywołać się nie więcej niż 4 razy.
function example() {
let a = 4;
const interval = setInterval( () => {
console.log( a-- );
if ( a === 0 ) {
clearInterval( interval );
}
}, 500 );
}
example();
Do napisania tej funkcji wykorzystałem funkcję setInterval
, która umożliwia na wywoływanie akcji w danym okresie czasowym. Po 4 wywołaniu przerywam interwał za pomocą funkcji clearInterval
.
Czym jest closure?
Closure to zjawisko, które ma miejsce, gdy funkcja wewnętrzna ma dostęp do zmiennych z funkcji zewnętrznej, nawet po zakończeniu wykonywania funkcji zewnętrznej. Najlepiej będzie to zobrazować przykładem.
function outer() {
const outerVar = 'outer var';
function inner() {
console.log(outerVar);
}
return inner;
}
const closureExample = outer();
closureExample(); // 'outer var'
Do czego służy tryb strict
w JS? Dlaczego go wprowadzono w takiej formie do standardu?
JavaScript znany jest z tego, że jest w niektórych miejscach niekonsekwentny, nieintuicyjny i można w nim znaleźć nietypowe zachowania. Aby częściowo rozwiązać opisany problem, wprowadzono tryb strict
. Przykładowo w trybie strict
zmienne muszą być deklarowane za pomocą słów kluczowych var
, let
lub const
, co poza trybem strict nie będzie powodowało błędów. Zachowań, które zmienia tryb strict
, jest więcej. Aby włączyć tryb strict
, należy zadeklarować to, korzystając z 'use strict'
.
Druga część pytania jest o tyle ciekawa, że odpowiedź idzie wydedukować. Wprowadzenie trybu strict jako domyślnego spowodowałoby, że wiele istniejących już stron WWW i skryptów przestałoby działać. Aby zachować kompatybilność wsteczną, tryb strict
należy włączyć samodzielnie.
Podsumowanie
Te kilkanaście pytań to o wiele za mało, by być dobrze przygotowanym do rozmowy rekrutacyjnej. Jeszcze raz gorąco zachęcam do sprawdzenia innych wpisów na blogu oraz pobranie e-booka 106 Pytań Rekrutacyjnych Junior JavaScript Developer. Zachęcam też do zostawiania komentarzy i udostępnienia tego wpisu znajomym, którym ta wiedza może się przydać.
Źródła i materiały dodatkowe
- Testowanie JavaScript – 3 najpopularniejsze frameworki
- Top JavaScript Testing Frameworks
- Top 11 JavaScript Testing Frameworks: Everything You Need to Know
- CSS display: inline vs inline-block
- HTML Standard – 13.5 Named character references
- Using the Fetch API
- Pure functions
- Using data attributes
- What is the difference between a function expression vs declaration in JavaScript?
- Understanding Functions and 'this’ In The World of ES2017
- Function expressions vs. function declarations
- What is the difference between properties and attributes in HTML?
- Why cookies dont expire after closing browser?
- The Web Storage API: local storage and session storage
- Pytanie rekrutacyjne nr 2: Jak optymalizowałbyś zasoby strony internetowej? — JavaScript Developer
- MDN – Lazy Loading
*Artykuł jest odświeżoną wersją wpisu z 2019 roku.
w pkt. 15. rozwiązaniu brakuje opcji natychmiastowego zatrzymania.Osobiście chyba dałbym, aby funkcja
example()
zwracała wartośćinterval
, aby można było wykonać:const inteval = example()
// clearInterval(interval)