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. 4
- 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ą.
Jaki będzie rezultat wywołania poniższego kodu? Dlaczego otrzymamy taki wynik?
function foo() {
for(var i=0; i<10; i++) {
setTimeout(function() {
console.log(i);
}, 300);
}
}
foo();
Jest to przykład kodu, który kilkukrotnie trafiłem w trakcie rozmów rekrutacyjnych. Odpowiedzią na to pytanie będzie wyświetlenie w konsoli 10 razy liczby 10. W pętli for
zmienna i jest zadeklarowana za pomocą słowa kluczowego var
. Zmienna var ma zasięg globalny w obrębie funkcji foo()
. Po upływie 300 milisekund od rozpoczęcia pętli zostanie wywołane 10 callbacków. W każdym callbacku console.log(i)
wypisze wartość zmiennej i
, która w tym momencie wynosi 10. Dlatego otrzymamy 10 wypisań liczby 10. Intuicja podpowiada odpowiedź „liczby od 0 do 9”, na którą sam raz dałem się złapać. Byłoby to prawdą, gdyby słowo var
zastąpić słowem kluczowym let
. Wtedy po 300 milisekundach zostaną wyświetlone liczby od 0 do 9.
W miejsce przedstawionego fragmentu może się pojawić inny, ale jego celem wciąż będzie sprawdzenie, czy kandydat jest świadom istnienia wybranych mechanizmów języka JavaScript.
Jaka jest różnica między klasą a obiektem?
Klasę może zdefiniować schemat obiektu do inicjalizacji, natomiast obiekt jest to powołany do życia byt na podstawie stworzonego wcześniej schematu (klasy).
Przykładowo tworząc klasę Car
opisującą samochód, będzie miał on cechy np. kolor, marka, rodzaj paliwa oraz metody np. jedź
, hamuj
, skręcaj
. Tworząc obiekt na podstawie klasy, definiuje się już konkretne właściwości, jakie ma mieć ten samochód. W przypadku Car
może być to na przykład czerwone Ferrari z silnikiem benzynowym.
Czym są wzorce projektowe?
Wzorce projektowe są sprawdzonymi i powtarzalnymi rozwiązaniami dla powszechnie występujących problemów w wytwarzaniu oprogramowania. Wzorce służą zapewnieniu efektywnych i optymalnych rozwiązań dla konkretnych sytuacji i pozwalają na tworzenie bardziej elastycznego, skalowalnego i zrozumiałego kodu. Praca z wykorzystaniem wzorców ma również pozytywny wpływ na komunikację w zespole. Przykładowo, podczas analizy danego problemu, fabryka czy budowniczy powie zespołowi więcej niż skomplikowany opis rozwiązania, czy próby wynalezienia koła na nowo.
Czym jest złożoność obliczeniowa?
Złożoność obliczeniowa określa, jak szybko rośnie lub maleje ilość zasobów obliczeniowych wymaganych do wykonania algorytmu w zależności od rozmiaru danych wejściowych. Złożoność pozwala na opisanie, jak efektywnie działa dany algorytm w zależności od wielkości problemu, na którym jest stosowany. O złożoności obliczeniowej więcej dowiesz się z mojego artykułu Złożoność obliczeniowa algorytmów,
Jakie znasz struktury danych?
Do najbardziej podstawowych struktur danych należą: stos, kolejka, lista, drzewo binarne i graf. Samych struktura danych jest oczywiście więcej. Tak jak w przypadku wzorców, praktyka zdecydowanie ponad teorię. Znanie definicji stosu nic Ci nie da, jeśli nie będziesz w stanie go wykorzystać w praktyce.
Czym jest destrukturyzacja?
Za pomocą destrukturyzacji w prosty sposób można przypisywać właściwości obiektów do lokalnych zmiennych. Destrukturyzacja pomocna jest też w przypadku importowania zmiennych, klas i metod z innych plików i paczek.
const person = {
name: 'John',
surname: 'Doe'
};
const { surname } = person;
console.log( surname ); // Doe
Dzięki destrukturyzacji nie jest konieczne każdorazowe odwoływanie się do obiektu person. Zamiast tego można odwołać się do lokalnej zmiennej surname. Zwiększa to istotnie czytelność kodu.
Wytłumacz na czym polega hoisting.
Hoisting występuje w przypadku zadeklarowania zmiennej lub funkcji słowem kluczowym var
. Hoisting powoduje, że deklaracje zmiennych są wywoływane w pierwszej kolejności przed wykonaniem reszty kodu. Co istotne, hoisting jedynie deklaruje zmienne, a nie przypisuje im wartości. Oznacza to, że odwołanie się do zmiennej zadeklarowanej słowem kluczowym var będzie skutkować otrzymaniem wartości undefined
.
Zmienne zadeklarowane słowami let
oraz const
również ulegają hoistingowi, lecz zamiast ich deklaracji, trafiają do tzw. Temporal Dead Zone. W przypadku odwołania się do zmiennej typu let
czy const
przed zadeklarowaniem otrzymamy błąd ReferenceError: variable_name is not defined
.
Dodawanie czcionek poprzez link
vs poprzez import
. Co wybierzesz?
Wybór może mieć znaczenie w kontekście wydajności i czasu ładowania strony WWW. Czcionki importowane przez @import
są ładowane sekwencyjnie. Natomiast czcionki ładowane przy pomocy link
są ładowane równolegle.
Czym jest normalizacja CSS i po co to robić?
Większość przeglądarek ma domyślne style dla elementów HTML. Poleganie na nich może powodować, że niektóre elementy mogą wyglądać inaczej na różnych przeglądarkach. Aby zapobiec temu zjawisku, można dokonać normalizacji CSS. Normalizacja CSS polega na ujednoliceniu domyślnego wyglądu elementów dla wszystkich przeglądarek. Alternatywą dla normalizacji jest reset stylów, czyli całkowite usunięcie domyślnych stylów z elementów HTML. Aby te mechanizmy działały poprawnie, style normalizujące lub resetujące powinny być załadowane jako pierwsze.
Jakiej funkcji użyjesz, by w JavaScript posortować osoby według wieku?
Sortowanie liczb jest dobrym przykładem zadania, którym można sprawdzić praktyczną znajomość JavaScriptu przez kandydata. Aby lepiej zobrazować, w czym tkwi problem, przedstawię dwa warianty zadania.
Pierwszym wariantem jest tablica liczb zawierająca wiek osób: [ 30, 25, 3, 7, 35 ]
. Intuicja podpowiada, by wykorzystać funkcję sort
. Jednak domyślne wywołanie funkcji sort
posortuje tablicę nieprawidłowo. Rezultatem będzie wynik [ 25, 3, 30, 35, 7 ]
. Wynika to z faktu, że funkcja sort
domyślnie sortuje wartości alfabetycznie. Prostym sposobem na posortowanie liczb względem wartości jest przekazanie funkcji sortującej jako parametr funkcji sort
.
console.log( [ 30, 25, 3, 7, 35 ].sort( ( a, b ) => a - b ) );
W drugim wariancie zadania, gdzie do posortowania jest lista obiektów opisujących osoby, sprawa jest równie prosta.
const people = [
{ name: 'Anna', age: 30 },
{ name: 'Jan', age: 25 },
{ name: 'Tomek', age: 3 },
{ name: 'Wanda', age: 7 },
{ name: 'Maria', age: 35 },
];
console.log( people.sort( ( a, b ) => a.age - b.age ) );
Jaka jest różnica między var
, let
i const
? Który jest preferowany?
Przede wszystkim to wcześniej wspomniane już zachowanie związane z hoistingiem. Oprócz tego zmienne typu let
oraz const
mogą tworzyć zasięg blokowy. Dodatkowo jak nazwa wskazuje, const
jest „wartością stałą”, czyli nie można takiej zmiennej ponownie przypisać wartości. Najbardziej preferowany w użyciu jest const
, w drugiej kolejności let
, a w przypadkach, gdy nie możemy ich użyć, pozostaje nam użycie var
. Wynika to z dobrych praktyk i zwiększonej czytelności kodu.
Zaimplementuj stos (Stack) w JS.
Zaproponowana implementacja stosu będzie zawierała dwie funkcje — jedna do wrzucania elementu na stos oraz droga do zdejmowania elementu ze stosu. Stos jest strukturą LIFO, czyli Last In First Out. Oznacza to, że ostatni umieszczony na stosie element jest z niego zdejmowany w pierwszej kolejności. Przykładowa implementacja będzie wyglądała następująco:
const stack = {
items: [],
push: item => stack.items.unshift(item),
pop: () => {
if (!stack.items.length ) {
console.warn( 'empty stack' );
return;
}
return stack.items.shift();
}
}
stack.push(9);
stack.push(4);
stack.push(1);
stack.push(5);
stack.push(5);
stack.push(16);
stack.push(2);
stack.pop();
stack.pop();
console.log(stack.items); //expected output: [5, 5, 1, 4, 9]
Opisz pokrótce, na czym polega podatność SQL Injection.
Podobnie jak w przypadku pytania o XSS z poprzedniego zestawu pytań, o ile nie aplikujesz na stanowisko ściśle związane z cyberbezpieczeństwem, wystarczy Ci znajomość podstawowych założeń oraz skutków, jakie może powodować istnienie tej podatności.
SQL Injection to podatność, która umożliwia atakującemu na wstrzyknięcie własnego fragmentu zapytania SQL do zapytania, jakie aplikacja wykonuje do bazy danych. Celem takiego ataku może być odczyt, dodanie, modyfikacja lub usunięcie danych, do który dany użytkownik nie powinien mieć dostępu. Cechą czyniącą dany system podany na atak przez wstrzyknięcie SQL jest to, że przesyłane wartości nie są poddawane walidacji, filtracji ani sanityzacji w poprawny sposób lub nie są poddawane tym procesom w ogóle.
Będąc przy podatności SQL Injection, warto wspomnieć, że wstrzyknąć złośliwy payload można nie tylko do zapytań SQL, ale też m.in. NoSQL, komend OS czy zapytań wywoływanych przez ORM.
Opisz pokrótce, na czym polega atak CSRF
Tak jak w przypadku poprzednich pytań związanych z cyberbezpieczeństwem, wystarczy Ci znajomość podstawowych założeń oraz skutki tego ataku.
Atak CSRF pozwala zmusić użytkownika aplikacji webowej do wykonania niepożądanych działań w aplikacji, w której jest obecnie uwierzytelniony. Daje to furtkę do dalszych działań na szkodę użytkownika, które są dostępne w ramach podatnej aplikacji. Atak ten często jest wykorzystywany do zmiany stanu aplikacji. Ponieważ rezultat niepożądanych zapytań zwracany jest ofierze, sam atak CSRF nie sprawdzi się dobrze do ataków polegających na kradzieży danych. Sytuacja ta ulegnie zmianie w przypadku, gdy wykorzystanie CSRF będzie wzbogacone wykorzystaniem innych podatności lub pozwoli atakującemu uzyskać dostęp do atakowanego systemu. Szczególnie niebezpieczne jest przeprowadzenie ataku CSRF na użytkownika z uprawnieniami administratora.
Jednym ze sposobów na przeciwdziałanie CSRF jest wprowadzenie do aplikacji tokena CSRF generowanego przez serwer, który jest następnie validowany przy wysyłaniu kolejnych zapytań do serwera. W rezultacie, zapytania niepodpisane lub podpisane niewłaściwym tokenem nie zostaną zaakceptowane przez serwer.
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
- ES6 variable scopes in loops with closure
- Difference between object and class
- Data Structures in JavaScript
- List of data structures
- stevesouders.com – don’t use @import
- What is the difference between Normalize.css and Reset CSS?
- Implementation of Stack in JavaScript
- OWASP – A03:2021 – Injection
- Czym jest podatność CSRF (Cross-Site Request Forgery)?
- Cross Site Request Forgery (CSRF)
- CSRF tokens: What is a CSRF token and how does it work?
*Artykuł jest odświeżoną wersją wpisu z 2019 roku.
Mam pytanie do ostatniego przykładu.
Jak przepisać to na ES6 z arrow function?
Robiąc to intuicyjnie zwraca mi „Cannot read property 'unshift’ of undefined”.
Domyślam się, że chodzi o „this” ale o co dokładnie kaman to nie wiem :/
Dobrze się domyśliłeś, że chodzi o this. Przerobienie tych metod na arrow function spowoduje, że this w tych metodach będzie wskazywał na window. Aby przerobić te metody na funkcje strzałkowe użyłem this przekazywanego w parametrze metody, gdzie jako parametr użyłem obiektu, którego dotyczy metoda.
Podrzucam kod z przykładem: https://jsbin.com/tuselukuwa/edit?js,console
Gdyby coś było niejasne, to służę pomocą 🙂
Ok, a czy zamiast przekazywać obiekt w metodzie oraz używać „this” i „that” nie sprawniej jest odwołać się bezpośrednio w obiekcie do niego samego? (Może to ma jakieś negatywne konsekwencje? Tego nie wiem :|)
Tak jak to tutaj zrobiłem: https://codepen.io/Rafi-R/pen/QoQLWE ?
Faktycznie, mój przykład wydaje się być nieco przekombinowany w porównaniu z Twoim.
W praktyce nasz kod jest identyczny, z tym że ja stack przekazywałem jako parametr a ty go użyłeś bezpośrednio. 🙂