1.02.20228 min

Redakcja Bulldogjob

JavaScript Developer – pytania rekrutacyjne + odpowiedzi

Dowiedz się, jak przygotować się do rozmowy kwalifikacyjnej JavaScript Developera i poznaj przykładowe pytania rekrutacyjne!

JavaScript Developer – pytania rekrutacyjne + odpowiedzi

JavaScript w 2021 r. po raz dziewiąty został wybrany przez ankietowanych StackOverflow Survey najpopularniejszym językiem programowania. Nic w tym dziwnego, skoro ma tak szerokie zastosowanie w aplikacjach webowych. Według naszego badania społeczności IT (zachęcamy do udziału w tegorocznej odsłonie) z JS korzysta na co dzień, aż 65% ankietowanych. Wskazali również ten język jako najbardziej znienawidzony, ponieważ zebrał 20% głosów.

Dlatego zdecydowaliśmy się zebrać dla Was najpopularniejsze pytania z rozmów technicznych na JavaScript Developera. Powinny też się przydać Fullstack Developerom, w końcu JavaScript jest wszędzie.


Podstawowe pojęcia

Na rozgrzewkę same podstawy, czyli krótkie przypomnienie z założeń samego języka. Wiemy, może to być dla Ciebie banalne, ale w stresie człowiek najczęściej zapomina właśnie tych najprostszych rzeczy.

Hosting

To wbudowany w JavaScript mechanizm wynoszący deklarację zmiennych bez ich inicjowania na początek funkcji.

Zakres zmiennych JavaScript:

  • letdeklaruje zmienną w zasięgu bloku, wraz z opcjonalnym zainicjalizowaniem wartością. 
  • const  deklaruje stałe w zasięgu bloku, podobne do słowa kluczowego let, ale nie można zmienić wartości tej stałej. Deklaracja const tworzy referencję typu read-only do wartości.
  • var deklaruje zmienną o aktualnym kontekście wykonania, wraz z opcjonalnym zainicjalizowaniem wartością.
  • zmienna bez żadnego modyfikatora jest uznawana za globalną.

Typy danych:

  • String — określa sekwencję tekstu jako ciąg znaków zamknięte w apostrofach.
  • Number- warto tu pamiętać, że liczby w JS to liczby zmiennoprzecinkowe podwójnej precyzji, zaimplementowane zgodnie z IEEE 754.
  • Boolean czyli klasyczne prawda/fałsz.
  • Array pozwoli odnieść się do wielu elementów z określonej tablicy.
  • Object praktycznie “wszystko” 😉
  • null oznacza wartość zerową.undefined najwyższa właściwość, która nie jest określona.
  • BigInt deklarowane przed dodanie n na końcu licz

Warto też pamiętać o API każdego z tych typów, szczególnie String, Array i Object, które często wykorzystuje się w codziennej pracy.


Co to jest DOM?

(Document Object Model) - określa umiejscowienie i rolę dokumentów tekstowych HTML w postaci modelu obiektowego w pamięci komputera. 


Czym jest domknięcie?

Domknięcie to mechanizm, który wiąże funkcję z otaczającym ją stanem. W praktyce pozwala na dostęp z funkcji wewnętrznej do zasięgu funkcji zewnętrznej.


Jaka jest różnica pomiędzy operatorami == oraz ===?

Operator == najpierw wykonuje przekształcenia typu i dopiero potem porównuje obydwie strony, dlatego zwróci true, jeżeli wartości po rzutowaniu na wspólny typ są takie same. Natomiast operator === nie wykonuje przekształceń typu, więc by zwrócił true potrzeba zgodności zarówno typu, jak i wartości.

Bułka z masłem — prawda? Podnieśmy poprzeczkę i przejdźmy do konkretnych pytań.


Pytania ogólne

W tej sekcji zebraliśmy najpopularniejszych pytań, jakie trafiają się na rozmowach kwalifikacyjnych dla developerów JavaScript. Część opatrzyliśmy przykładowymi odpowiedziami, a część powinieneś opracować sam — wiesz, tak dla wprawy 😁 


Czy JavaScript wspiera dziedziczenie? Jeżeli tak, to w jaki sposób?

JS jest językiem opartym na prototypach, a nie klasach. Co prawda od ES6 dodano pojęcie klas, jednak jest to lukier składniowy. Pod spodem nadal mamy do czynienia z łańcuchem prototypów. Prototyp obiektu jest również obiektem (lub nullem, jeżeli dojedziemy do końca łańcucha), którego właściwości można modyfikować, co przekłada się na właściwości dostępne dla pierwotnego obiektu. W ten sposób można dziedziczyć w JavaScript.


Czym jest pętla zdarzeń w JavaScript?

Pętla zdarzeń to mechanizm, który umożliwia asynchroniczne wykonanie, mimo, że JavaScript jest jednowątkowy. Właściwa praca wykonuje się w stosie wywołań, tam funkcje JS są wykonywane. Wiadomość o tym co powinno zostać przetworzone nie ląduje jednak samoczynnie na stosie wywołań. Najpierw te wiadomości trafiają do kolejki, gdzie umieszcza je środowisko uruchomieniowe. Pętla zdarzeń natomiast ciągle nasłuchuje nowych wiadomości w kolejce i wysyła je do stosu wywołań, jeżeli tylko stos jest wolny. Taka implementacja mogłaby sprawiać spore problemy, jeżeli stos wywołań zostałby zablokowany przez czekanie na zasoby, jednak praktycznie wszystkie operacje I/O w JS są nieblokujące (z wyjątkiem kilku wywołań).


Czym jest IIFE?

Immediately Invoked Function Expression — to funkcja, którą da się wywołać zaraz po swojej definicji. IIFE  jest opakowywane w okrągłe nawiasy, po których następują kolejne okrągłe nawiasy - ma to na celu wywołanie zdefiniowanej funkcji. Więcej o tym zagadnieniu poczytasz tutaj.


Co to jest “callback hell” i jak można się go pozbyć?

Piekło wywołań objawia się wtedy, gdy mamy do czynienia z kaskadowym wywołaniem funkcji zwrotnych. Prowadzi ono do kodu trudnego w zrozumieniu i debugowaniu. Jest kilka technik, które pomagają w opanowaniu piekła wywołań. Samo wyłączenie callbacków jako osobne funkcji może być pomocne, jednak chyba najciekawsze są rozwiązania później dodane do JS - obietnice i async/await.


Czym jest obietnica (promise)?

Obietnice to obiekty, które reprezentują wykonanie (sukcesem lub porażką) operacji asynchronicznej. Są tak nazwane, bo wykonywana funkcja asynchroniczna musi złożyć obietnicę dostarczenia w przyszłości wartości. Po zakończeniu operacji asynchronicznej jej wynik może zostać obsłużony przez wywołanie na obietnicy metody then.


Czym są async/await?

Obydwa słowa kluczowe wiążą się z obietnicami. Pierwsze z nich - async - wstawione przed deklaracją funkcji sprawi, że taka funkcja zwróci obietnicę. Drugie z nich - await - może wystąpić tylko wewnątrz funkcji poprzedzonej przez async i powinno się znaleźć przed wykonaniem funkcji asynchronicznej. Sprawi, że wykonanie nie przejdzie dalej, dopóki obietnica nie zostanie wykonana. Dzięki temu asynchroniczny kod przypomina swoim zachowaniem kod synchroniczny.


Jak możesz udostępniać kod między plikami?

Należy utworzyć ES6 w komponencie usługi i eksportować zmienne lub funkcje, które mają być współdzielone. Moduł ES6 jest plikiem, który jawnie eksportuje zmienne lub funkcje, z których mogą korzystać inne moduły. 


Czym jest operator ?

Operator to tzw. operator rozwinięcia. Dzięki niemu możliwe jest rozwinięcie ciągu znaków w przypadkach, kiedy wiadomo, że będzie tam zero lub więcej argumentów dla funkcji, lub elementów dla literałów tablicowych. Można w ten sposób łatwo kopiować istniejącą już tablicę lub obiekt w inne tablice lub obiekty. Odwrotnością spread operatora jest operator rest, dzięki któremu jedna funkcja może przyjąć wiele argumentów, a tablice lub obiekty zostać scalone w jeden. 


Czym jest currying i w jakich sytuacjach się go stosuje?

Currying to technika pracy z funkcjami, polegająca na ich transformacji w następujący sposób: f(a, b, c) do f(a)(b)(c). Dzięki temu de facto rozbija funkcję mającą wiele parametrów do wielu funkcji mającej po jednym parametrze. Więcej na ten temat przeczytać można tutaj.


Jaka jest definicja funkcji wyższego rzędu?

Funkcja wyższego rzędu to funkcja, która przyjmuje inne funkcje jako parametry lub też taka, której wynik również jest funkcją. Przykładowa funkcja wyższego rzędu to map służąca do zwracania listy z wynikami funkcji dla wszystkich elementów z danej listy.


Jakie są zalety i wady rozszerzania wbudowanych obiektów JavaScript?

Rozszerzanie wbudowanych obiektów uważane jest za złą praktykę. W ten sposób zmieniamy zachowanie obiektów. I o ile mowa o obiekcie, z którego będzie działał tylko konkretny kod, to problem nie jest duży, ale w przypadku zewnętrznego kodu, który wykorzysta rozszerzony obiekt, nic nie będzie działać. Dobrą praktyką jest korzystanie ze wbudowanych metod. Dodawanie własnych metod do obiektów może być problematyczne także ze względu na interferencje z bibliotekami, które same mogą rozszerzać obiekty o te same metody. Wówczas dojdzie do konfliktu, nadpisania, i kod po prostu nie będzie działał. Zaletą rozszerzania wbudowanych typów może być natomiast to, że można zbudować w ten sposób polyfill, przez co kod będzie działał również na starszym oprogramowaniu, przeglądarkach nieobsługujących nowszych wersji JavaScript.


Jakich konstrukcji językowych używasz do iteracji po elementach tablicy i właściwościach obiektów?

W przypadku tablic najlepszym wyborem będzie pętla for...of, dzięki której można dokonywać iteracji nie tylko na tablicach, ale na wszystkich obiektach, w których jest to możliwe. Ponadto dzięki break i continue nie trzeba każdorazowo iterować po całej tablicy. Jeśli zaś chodzi o obiekty, to wykorzystuje się for…in, choć warto rozważyć także konwersję obiektu do tablicy. Skorzystać można także z pętli for…each, która wykonuje wywołanie zwrotne dla każdego elementu obiektu.


Czym są funkcje strzałkowe i czym się różnią od zwykłych funkcji?

To funkcje o krótszej składni, wykorzystującej znak => (stąd nazwa). Ponadto w funkcjach strzałkowych nie występuje oddzielnie this, używany jest kontekstu wykonania funkcji.


Zadania praktyczne

Często spotykane są też zadania praktyczne. W czasie rozmowy często będą polegać na doprowadzeniu do działania już zaczętego projektu. Wtedy do dyspozycji jest często już napisany zestaw testów, który powinien przechodzić po dodaniu odpowiedniego kodu. W niektórych organizacjach są też zadania domowe, które jednak nie powinny zająć dłużej niż 2-3 godziny kodowania. Zwykle tego typu zadania będą skupiać się na wykorzystaniu teorii, o którą wcześniej pytano w sekcji teoretycznej.

Może się również pojawić pytanie w stylu ”wyciągnij wnioski z tego kodu”, czyli otrzymasz wydrukowany kawałek kodu i zostaniesz poproszony o ustalenie, czy jest poprawny / co ten kod robi itd – przykłady takich zadań zamieszczamy na końcu artykułu. Zwykle tego rodzaju zadania bazują na zauważeniu jednego lub kilku kruczków języka. Dlatego przed rozmową warto sobie powtórzyć konstrukcje języka. Oto popularne przykłady, jakie można napotkać w procesie rekrutacji.


Jakie rezultaty będzie miał następujący kod?

var myObject = {
    foo: "bar",
    func: function() {
        var self = this;
        console.log("outer func:  this.foo = " + this.foo);
        console.log("outer func:  self.foo = " + self.foo);
        (function() {
            console.log("inner func:  this.foo = " + this.foo);
            console.log("inner func:  self.foo = " + self.foo);
        }());
    }
};
myObject.func();

W zewnętrznej funkcji this i self odnoszą się do myObject. Mogą mieć zatem referencję i dostęp do foo. Wewnętrzna funkcja natomiast nie odnosi się do myObject, przez co this.foo pozostaje niezdefiniowane, zaś self.foo pozostaje w zakresie i utrzymuje dostęp.


Jakie rezultaty będzie miał ten kod?

function test() {
   console.log(a);
   console.log(foo());  

   var a = 1;
   function foo() {
      return 2;
   }
}

test();

Co ważne zmienna i funkcje są tutaj windowanie, czyli przenoszone na samą górę – do zakresu funkcji lub w ogóle do globalnego zakresu zmiennych. Zatem w momencie wydrukowania zmienna a jest zdeklarowana w funkcji, ale wciąż jest niezdefiniowana. Wynikiem będzie zatem undefined i 2.


Jak opróżnić tablicę?

Można to zrobić na wiele sposobów, zerując jej długość: arrayList.length = 0;. Możliwe jest także użycie arrayList = [];, co sprawi, że wartością zmiennej arrayList będzie nowy obiekt Array. Należy tu jednak pamiętać, że drugą metodę można stosować tylko wtedy, gdy do pierwszego obiektu Array nie ma w kodzie żadnych referencji.

Przykłady rzecz jasna można mnożyć, dlatego warto samodzielnie śledzić internetowe bezy danych. Na sam koniec, dla uzupełnienia lub odświeżenia, podrzucamy kilka przydatnych linków:

<p>Loading...</p>