Rozmowa kwalifikacyjna #08 – Pytania – JavaScript
Rozmowa kwalifikacyjna #08 – Pytania – JavaScript

Nadszedł w końcu czas na pytania, które możesz spotkać podczas rekrutacji. Poniżej przedstawiam wszystkie pytania z JS, o jakich słyszałem, jakie spotkałem lub jakie zadawałem podczas rekrutacji. Nie przedłużając – zapraszam do lektury.


Jest to ósma część serii „Rozmowa Kwalifikacyjna”. Zapraszam do pozostałych artykułów:
Rozmowa kwalifikacyjna #01 – Wstęp
Rozmowa kwalifikacyjna #02 – Przygotowanie się do rozmowy
Rozmowa kwalifikacyjna #03 – Początek rozmowy, część miękka i porady ogólne
Rozmowa kwalifikacyjna #04 – Ogólna rozmowa techniczna
Rozmowa kwalifikacyjna #05 – Pytania do firmy
Rozmowa kwalifikacyjna #06 – Kontakt i feedback
Rozmowa kwalifikacyjna #07 – Podpisanie umowy


1. Jakie są podstawowe typy zmiennych w JS?

W zwykłym JSie jest 6 podstawowych typów:

  • Number
  • Object
  • String
  • Boolean
  • Null
  • Undefined

W ES6 doszedł siódmy typ:

  • Symbol

2. Co to są prymitywy (primitive) i typy złożone?

Prymitywy to zmienne, które nie są obiektami i nie mają funkcji:

  • String
  • Number
  • Boolean
  • Null
  • Undefined
  • Symbol (ES6)

Typy złożone to przeciwieństwo prymitywów. Jest w sumie tylko jeden taki typ:

  • Object. Ciekawostka, Array to Object. Co prawda Object ze specjalnymi właściwościami, ale jednak Object.

3. Co to jest scope? Jakie mogą być zakresy?

Generalna odpowiedź jest taka – Scope to zasięg (obszar, część kodu), w którym zadeklarowane zmienne i funkcje są widoczne.

Można usłyszeć o 4 rodzajach scope:
Local scope – zasięg lokalny, czyli zmienna jest widoczna tylko w lokalnym bloku kodu czy funkcji, a po wyjściu z tego kawałka kodu będzie niewidoczna. Przykład – lokalna zmienna w funkcji.
Function scope – przykład lokalnego scope – zasięg funkcyjny, czyli zmienna lub funkcja jest widoczna w funkcji, w której zostały stworzone.
Block scope – przykład lokalnego scope – zasięg blokowy, czyli zmienna jest widoczna w bloku kodu, w którym została stworzona.
Global scope – zasięg globalny, czyli zmienna lub funkcja jest widoczna wszędzie.

4. Dlaczego nie powinno się niczego dodawać do global scope?

Dodanie zmiennych i funkcji do global scope skutkuje:

  • Zwiększeniem zapotrzebowania na pamięć, a co za tym idzie, spowalnia stronę. Zmienne zwykle są usuwane po skończeniu się swojego scope, jednak nie w wypadku global scope. Zmienne są zawsze, więc czy ich potrzebujemy, czy nie, zawsze są załadowane.
  • Możliwością kolizji nazw. Kiedy dodajemy zmienne o generycznych nazwach w stylu „data”, istnieje możliwość, że inna używana przez nas biblioteka lub inny kawałek naszego kodu również używa takiej zmiennej, co może doprowadzić do problemów ze złymi danymi.

5. Czym się różni deklaracja zmiennych przez var, let, oraz const? Jakie mają zakresy?

var to domyślny sposób deklaracji zmiennych w JSie. Zmienne deklaruje się tak:

var zmienna;


Nowe wartości mogą być przypisywane do zmiennej dowolnie, a sama zmienna jest function-scoped, czyli jest widoczna w funkcji, w której się ją deklaruje. Przykład:

function test() {
  var zmienna = 0;
}
console.log(zmienna); // ReferenceError: zmienna is not defined

Zmienne stworzone za pomocą var nie są też block-scoped, czyli nie są widoczne tylko w bloku, w którym zostały zadeklarowane. Przykład:

var zmienna = 0
if (true) {
  var zmienna = 1;
}
console.log(zmienna); // 1

let i const to słowa kluczowe, które zostały wprowadzone w ES6 (ES2015). Pozwalają na tworzenie zmiennych tak jak var, jednak mają parę podstawowych różnic. Zmienne tworzy się tak:

let zmienna1;
const zmienna2 = 'wartość';

Zmienne let i constblock-scoped czyli są widoczne tylko w bloku, w którym zostały zadeklarowane.

let zmienna = 0;
if (true) {
  let zmienna = 1;
  const stala = 0;
}
console.log(zmienna); // 0
console.log(stala); // Uncaught ReferenceError: stala is not defined

Co więcej, na obydwie zmienne nie działa hoisting. Przykład:

console.log(a); // undefined
var a = 2;
console.log(b); // Uncaught ReferenceError: b is not defined
console.log(c); // Uncaught ReferenceError: c is not defined
let c = 2;

const dodatkowo nie pozwala na zadeklarowanie zmiennej bez jej inicjalizowania, zabrania też przypisywania wartości na nowo. Pozwala tylko zmieniać wartości tablic i obiektów:

const zmienna = 0;
zmienna = 1; // TypeError: Assignment to constant variable.
const zmienna2; // SyntaxError: Missing initializer in const declaration
const obiekt = {};
obiekt.zmienna = 1;
console.log(obiekt); // { zmienna: 1 }
obiekt = {} // TypeError: Assignment to constant variable.

Najlepiej używać let jak var, a const w przypadku, kiedy wiemy, że nie będziemy zmieniali danej zmiennej. let to taki nowy var. const to stała jak np. liczba Pi.

6. Czym jest „hoisting”?

Hoisting to mechanizm JavaScriptu, który przenosi wszystkie deklaracje zmiennych czy funkcji na początek ich scope, czyli albo na początek funkcji, albo do zasięgu globalnego. W praktyce wygląda to tak:

console.log(zmienna2); // ReferenceError: zmienna2 is not defined
console.log(zmienna); // undefined
var zmienna = 0;

zmienna2 nigdy nie jest zdefiniowana, za to zmienna jest, gdyż dzięki hoistingowi jej deklaracja idzie na początek scope. Tak wygląda powyższy kod po działaniu hoistingu:

var zmienna;
console.log(zmienna2); // ReferenceError: zmienna2 is not defined
console.log(zmienna); // undefined
zmienna = 0;

Można się spotkać też z nazwą Windowanie – deklaracje w JSie są windowane na początek ich scope.

7. Co to jest EcmaScript? Co to jest ES6? Jakie znasz nowości z ES6?

EcmaScript to standard języka programowania, który implementuje język JavaScript. Przykładem innej implementacji standardu jest ActionScript, skupmy się jednak na JSie.

ES6 czy inaczej ES2015, EcmaScript 6 lub EcmaScript 2015, to kolejna, szósta już wersja standardu. Od wydania wersji piątej minęło sporo czasu, po drodze JS zyskał ogromną popularność, więc należało coś z tym zrobić. Powstała więc szósta wersja standardu, gdzie dodano wiele naprawdę przydatnych funkcji. Według mnie 5 najważniejszych z nich to:

  • Klasy
  • Dziedziczenie
  • Fat arrow functions
  • let i const
  • Moduły

8. Czy istnieje dziedziczenie w JavaScript?

W zwykłym najczęściej spotykanym JavaScripcie (czyli JS przed ES6/ES2015) nie ma dziedziczenia znanego z innych języków obiektowych, np. z Javy czy C++. Jedyny sposób na dziedziczenie w tym JSie to dziedziczenie przez wykorzystanie prototypów.

W JSie nie ma klas, w zasadzie wszystko jest obiektem, a obiekty posiadają swój prototyp, czyli obiekt bazowy obiektu. Możemy to wykorzystać, tworząc obiekt jednego prototypu, używając innego. Przykład:

function Pracownik(name) {
    this.name = name;
}
 
Pracownik.prototype.przywitanie= function() {
    return "Witaj, nazywam sie " + this.name;
}
 
function Programista(name) {
    this.name = name;
    this.type = "programista";
}
 
Programista.prototype = Object.create(Pracownik.prototype);
Programista.prototype.constructor = Programista;
 
Programista.prototype.programuj= function() {
    return "Programuje!";
}
 
const programista = new Programista("Adam");
console.log(programista.programuj()); // Programuje!
console.log(programista.przywitanie()); // Witaj, nazywam sie Adam
 
const pracownik= new Pracownik('Tomek');
pracownik.programuj(); //błąd, pracownik nie ma funkcji programuj()

Najpierw stworzyłem funkcję bazową Pracownik i dodałem jej funkcję przywitanie(), która wypisuje nam „Witaj, nazywam się <imie>”. Następnie tworzymy inną funkcję, Programista, która poza imieniem ma też typ wykonywanej pracy. W linii 14 dzieje się magia dziedziczenia – zamieniamy prototyp obiektu Programista na prototyp obiektu Pracownik. Teraz obiekt Programista ma taki sam prototyp jak Pracownik, łącznie z konstruktorem. Aby zapobiec problemom, podmieniamy konstruktor na konstruktor Programisty w linii 15.

Teraz już bez przeszkód możemy używać, tworzyć obiekty Programista. Będzie on posiadał wszystko to, co dodamy do jego prototypu, oraz wszystko z prototypu obiektu Pracownik. Ważna jest tutaj kolejność działań.

Dziedziczenie znane z języków obiektowych weszło do JavaScriptu dopiero w ES6. Wygląda to następująco:

class Pracownik {
    constructor(name) {
        super()
        this.name = name;
    }

    przywitanie() {
        return "Witaj, nazywam sie " + this.name;
    }
}

class Programista extends Pracownik {
    constructor(name) {
        super(name);
    }

    programuj() {
        return "Programuje!";
    }
}

const programista = new Programista('Andrzej');
console.log(programista.programuj());

9. Jaka jest różnica między null, undefined i undeclared?

undeclared – zmienna lub funkcja nie została zadeklarowana wcześniej.

console.log(a);
 
// Uncaught ReferenceError: a is not defined

undefined – zmienna lub funkcja została zadeklarowana, ale nie ma żadnej wartości przypisanej: Skoro nie ma żadnej wartości, to typ zwracany przez undefined to undefined.

var a;
console.log(a); //undefined
console.log(typeof a); // undefined

null – wartość która może przyjąć zmienna, reprezentuje brak wartości. Ponieważ jest to wartość, typem, jaki zwraca null jest Object.

var a = null;
console.log(a); //null
console.log(typeof a); //object

10. Co to są funkcje anonimowe?

Są to funkcje, które nie mają nazwy i są przypisane tylko do miejsca w kodzie, w którym są tworzone. Przykład:

var nowaFunkcja = function() {
    console.log("Funkcja anonimowa");
}

Do zmiennej nowaFunkcja przypisaliśmy nową funkcję, jednak nie ma nigdzie jej nazwy (bo i po co, skoro od razu ją przypisujemy).

11. Co to jest IIFE?

IIFE czyli Immediately-Invoked Function Expression – jest to funkcja, która wywoła się od razu po skończeniu jej czytania przez interpreter. Ważne jest też to, że tworzy w sobie domknięcie, czyli niezależną przestrzeń funkcji. Dzięki temu unikami deklaracji zmiennych o takich samych nazwach – w końcu wszystko jest w funkcji. Przykład:

(function() {
  var zmiennaGlobalna = "globalna zmienna";
  console.log(zmiennaGlobalna); // globalna zmienna
})();

console.log(zmiennaGlobalna); // error

12. Czym są domknięcia (closure) w JS?

W internecie można się natknąć na wiele definicji tego, czym jest closure. Jest to jednocześnie trudne i łatwe zagadnienie. Jeżeli nie do końca podoba ci się moja definicja — spokojnie. To normalne. Zacznijmy od prostej definicji:

Closure (domknięcie) – jest to mechanizm pozwalający stworzyć funkcję, która pamięta swój scope / zasięg zmiennych / środowisko, z którego pochodzi, oraz ma do niego dostęp, nawet jeżeli jest wywołana poza nim.

Lub prościej:

Closure (domknięcie) to kombinacja funkcji i środowiska jej stworzenia.

Przykład:

function funkcjaZewnetrzna() {
    var zmiennaZewnetrzna = 123;
  
    function funkcjaWewnetrzna() {
	console.log(zmiennaZewnetrzna);
    }
  
    return funkcjaWewnetrzna;
}

var closure = funkcjaZewnetrzna();
closure(); // 123

Tworzymy funkcjaZewnetrzna(), która zwraca funkcjaWewnetrzna(). Nie zwracamy wywołania funkcji i przypisujemy nowej wartości do zmiennej, a zwracamy całą funkcję. funkcjaWewnetrzna() wypisuje zmiennaZewnetrzna. Teraz kiedy wywołujemy funkcjaZewnetrzna(), otrzymujemy funkcję funkcjaWewnetrzna() pod zmienną closure. W teorii nie powinna zadziałać, bo funkcja funkcjaWewnetrzna() sama z siebie nie ma zmiennaZewnetrzna:

function funkcjaWewnetrzna() {
    console.log(zmiennaZewnetrzna);
}

var closure = funkcjaWewnetrzna();
closure(); // ReferenceError: zmiennaZewnetrzna is not defined

Jednak po uruchomieniu funkcji widzimy w konsoli 123. Dzieje się tak, ponieważ funkcja funkcjaWewnetrzna() zapamiętuje swój scope, dzięki czemu ma dostęp do zmiennych ze scope. Normalnie funkcja po użyciu zostałaby usunięta z pamięci, jednak closure nie pozwala na to.

Closure zamyka część danych w oddzielnym scope. Nie zaśmiecają nam naszego głównego scope, z drugiej strony nie możemy ich zmienić z poziomu naszego scope inaczej niż przez funkcje, więc świadomie. Wychodzi na to, że tworzymy zmienne prywatne 🙂

Inne definicje Closure, które można spotkać:

  • Funkcja ze stanem
  • Funkcja, która ma dostęp do zewnętrznej zmiennej w swoim zasięgu.
  • Funkcja która „pamięta” i ma dostęp do swojego zasięgu zmiennych i jest wywoływana poza tym zasięgiem.
  • Funkcja wraz ze swoim środowiskiem
  • Domknięcie to funkcja, która pamięta swój leksykalny zasięg i ma do niego dostęp, nawet gdy zostanie wykonana poza nim.
  • Domknięcie jest tworzone, kiedy funkcja wewnętrzna zostaje zwrócona za zewnątrz swojego leksykalnego zasięgu.

13. Czym jest this w JS?

Jest to bardzo trudne pytanie. this jest pewnego rodzaju wskaźnikiem na kontekst, w którym jest wywoływany lub, inaczej mówiąc, pokazuje na miejsce, do którego należy. Taki wskaźnik na opakowanie, w którym się wskaźnik znajduje. Zależnie od miejsca wywołania this może wskazywać na cos innego:

this poza jakąkolwiek funkcją dla przeglądarek zawsze będzie wskazywało obiekt window. Tak samo, jeżeli użyjemy this w funkcji bezpośrednio pod obiektem globalnym:

console.log(this);

var wypiszThis = function() {
  console.log(this);
}

wypiszThis();

Jeżeli spróbujemy zrobić to samo, ale używając trybu restrykcyjnego („use strict”), próba wypisania this wygeneruje błąd, a samo this będzie undefined:

“use strict”;
console.log(this); // SyntaxError: Invalid or unexpected token

var wypiszThis = function() {
  console.log(this);
}

wypiszThis(); // SyntaxError: Invalid or unexpected token

W metodzie obiektu this wskaże na obiekt:

var obiekt = {
  zmienna:1,
  funkcja: function() {
    console.log(this)
  }
}

obiekt.funkcja(); // obiekt

Przy eventach, this wskazuje na obiekt wywołujący event:

<button onclick="this.style.color='red'">Zmień kolor!</button>

this w funkcji strzałkowej (arrow function, fat arrow, => ) zawsze pokazuje to, co funkcja wyżej:

var funkcja = function() {
  var zmienna = 1;
  
  console.log(this); // object Window
  
  var funkcjaStrzalkowa = () => {
    console.log(this); // object Window, to samo co w funkcji wyżej
  }
  
  funkcjaStrzalkowa();
}

funkcja();

Przy użyciu funkcji takich jak .bind, .call czy .apply, this wskazuje na przekazany obiekt:

var obiekt = {
  zmienna: "obiekt",
  metoda: function(numer) {
    console.log('Podany numer to - ' + numer)
    console.log(this);
  }
};

var noweThis = {
  zmienna: "nowy obiekt"
};

obiekt.metoda(5); // numer - 5, zmienna - "obiekt"
obiekt.metoda.call(noweThis, 7); // numer - 7, zmienna - "nowy obiekt"
obiekt.metoda.apply(noweThis, [9]); // numer - 9, zmienna - "nowy obiekt"
var nowaMetoda = obiekt.metoda.bind(noweThis, 11);
nowaMetoda(); // numer - 11, zmienna - "nowy obiekt"

Podczas tworzenia nowego obiektu przez słowo new, this wskazuje na nowo stworzony obiekt. Jest to tak ważna reguła, że nadpisuje nawet .bind:

var noweThis = {
  imie: 'Nie Andrzej'
};

function Osoba(imie) {
  this.imie = imie;
  
  console.log(this)
}

const osoba1 = new Osoba('Jan');
const osoba2 = new (Osoba.bind(noweThis))('Andrzej');

Podsumowując:

  • this bez niczego wskazuje w przeglądarce na obiekt Window, używając jednak strict mode, this wskazuje na undefined.
  • przy użyciu this w metodzie obiektu, this wskazuje na obiekt, którego metody używamy
  • przy użyciu this w evencie, this wskazuje na element wywołujący event.
  • przy funkcji strzałkowej this jest takie samo jak w funkcji wyżej
  • przy bind, call, apply this wskazuje na przekazany obiekt
  • przy tworzeniu obiektu przez new, this wskazuje na nowo utworzony obiekt

14. Czym jest Function.prototype.bind lub .bind()?

Jest to metoda funkcji, która tworzy nową funkcję z nowym this, przekazanym jako parametr w .bind. Przykład:

function Osoba(i) {
	this.imie = i;

	this.przywitanie = function() {
      console.log('Witaj, nazywam sie ' + this.imie)
    }
}

var osoba1 = new Osoba('Andrzej');
osoba1.przywitanie(); // Witaj, nazywam sie Andrzej

var osoba2 = new Osoba('Jan');
osoba2.przywitanie(); // Witaj, nazywam sie Jan

var nowaFunkcja = osoba1.przywitanie.bind(osoba2);
nowaFunkcja(); // Witaj, nazywam sie Jan

nowaFunkcja jest całkowicie nową funkcją, powstałą przez wzięcie funkcji przywitanie z osoba1 i zmianę this na osoba2 (zamiast starego osoba1). Dlatego po wywołaniu tej funkcji otrzymujemy imię z osoba2, a nie z osoba1.

15. Jak rozszerzyć wbudowane obiekty w JS? Czy powinno się to robić?

Wbudowane obiekty, takie jak String, można rozszerzyć przez prototypy:

String.prototype.witaj = function(){
 console.log('Witaj');
}
'test'.witaj();

Nie jest to jednak zalecane, gdyż różne biblioteki mogą rozszerzać obiekty wbudowane funkcjami o takich samych nazwach, jakimi ty rozszerzasz. Doprowadzi to wtedy do konfliktu nazw i błędów z tym związanych.

16. Jaka jest różnica między: function Obiekt(){}, var obiekt = Obiekt(), i var obiekt = new Obiekt()?

function Obiekt(){} – jest to deklaracja funkcji. Funkcja została dodana do Global Scope, ale nie została wykonana.

var obiekt = Obiekt() – jest to wykonanie funkcji. Tworzymy zmienną obiekt i przypisujemy do niej wykonanie funkcji Obiekt().

var obiekt = new Obiekt() – jest to konstruktor funkcji. Przez użycie słówka new tworzymy nową instancję klasy Obiekt i przypisujemy ją do zmiennej obiekt.

17. Różnica między == i ===?

== i === to operatory porównania. Różnica między nimi jest taka:

  • == porównując 2 rzeczy o różnych typach, próbuje sprowadzić je do jednego typu i dopiero porównać. Inaczej mówiąc, porównuje wartość.
  • === sprawdza od razu czy typ jest taki sam, jeżeli nie, to porównanie równe jest false. Inaczej mówiąc, porównuje wartość i typ.

Ciekawostka – porównanie 2 obiektów jest trudne. Sprawdź:

var a = {x: 1};
var b = {x: 1};
var c = a;
var d = {...a};

console.log( a == b ); // false
console.log( a === b ); // false
console.log( a == c ); // true
console.log( a === c ); // true
console.log( a == d ); // false
console.log( a === d ); // false

Zwykłe prymitywy są porównywane przez wartość. Obiekty są porównywane przez referencje, czyli przez adres, gdzie obiekt leży w pamięci. Dlatego a == b pokazuje false, w końcu dwa różne obiekty to dwa różne miejsca w pamięci. Dokładnie to samo zachodzi przy porównaniu a i d. Przy c jest inaczej. Przypisanie var c = a; przypisuje do zmiennej c referencje do obiektu a, a nie kopiuje obiekt. Dlatego porównanie jest prawdziwe. Problem polega na tym, że zmiana obiektu a lub c wpływa na obydwie zmienne:

var a = {x: 1};
var c = a;

console.log( a == c ); // true
console.log( a === c ); // true

a.x = 2; // 2
console.log(c.x);
console.log( a == c ); // true
console.log( a === c ); // true

c.x = 3;
console.log(a.x) // 3
console.log( a == c ); // true
console.log( a === c ); // true

18. Co robią i jaka jest różnica między .call i .apply?

.call i .apply to 2 sposoby zmiany kontekstu funkcji, czyli przekazania nowego this i parametrów do funkcji. Przykład:

var obiekt = {
  zmienna: "obiekt",
  metoda: function(numer1, numer2, numer3) {
    console.log('Podane numery to - ' + numer1 + ', ' + numer2 + ', ' + numer3)
    console.log(this);
  }
};

var noweThis = {
  zmienna: "nowy obiekt"
};

obiekt.metoda(1,2,3); // numery - 1, 2, 3, zmienna - "obiekt"
obiekt.metoda.call(noweThis, 4,5,6); // numery - 4, 5, 6, zmienna - "nowy obiekt"
obiekt.metoda.apply(noweThis, [7,8,9]); // numery - 7, 8, 9, zmienna - "nowy obiekt"

Jak widzisz, zmieniamy kontekst dzięki obydwu funkcjom. Różnica między nimi jest taka, że inaczej przyjmują parametry funkcji. W metodzie .call wszystkie parametry są po przecinku, gdzie pierwszy to nowe this:

obiekt.metoda.call(noweThis, 4,5,6); // numery - 4, 5, 6, zmienna - "nowy obiekt"

W metodzie .apply są 2 parametry: nowe this oraz tablica z argumentami funkcji:

obiekt.metoda.apply(noweThis, [7,8,9]); // numery - 7, 8, 9, zmienna - "nowy obiekt"

19. Co to jest delegacja eventów (event delegation)?

Event delegation to mechanizm, pozwalający reagować na zdarzenia/eventy poprzez element rodzica, a nie przez wszystkie elementy dzieci oddzielnie.

Spójrzmy na przykład:

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>

Widzimy prostą listę. Teraz, żeby dodać do każdego elementu event na kliknięcie, można zrobić coś takiego:

<ul>
    <li onclick="console.log(event)">1</li>
    <li onclick="console.log(event)">2</li>
    <li onclick="console.log(event)">3</li>
</ul>

Działa. Mimo że jest mało czytelne, działa. Teraz przychodzi do was szef i prosi, żeby zamiast 3 elementów lista miała np. sto. Sto elementów to sto onclick w pamięci, czyli niepotrzebnie zaśmiecona pamięć. Zamiast tego, można to zrobić tak:

<ul onclick="console.log(event)">
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>

Mamy jeden event na rodzicu, który obsługuje nam jakiekolwiek kliknięcie rodzica czy dzieci. Jeden event zamiast np. stu. Działa tak samo, za to pamięć jest niezaśmiecona i strona działa szybciej.

20. Co to są Callback i Promises ? Czym się od siebie różnią?

Callback, inaczej wywołanie zwrotne, to funkcja, która jest przekazywana do innej funkcji i wywoływana w odpowiednim momencie, zwykle na koniec działania drugiej funkcji. Przykład:

function naukaDoRozmowy(zagadnienie, callback) {
  console.log('Aktualnie pracuje nad - ' + zagadnienie);
  callback();
}

naukaDoRozmowy('callback i promises', function() {
  console.log('I swietnie sie bawie');
});

W funkcji naukaDoRozmowy() dodaliśmy funkcję jako ostatni parametr i wywołujemy ją na koniec wykonania głównej funkcji. Teraz, przy uruchomieniu funkcji naukaDoRozmowy() należy podać tę funkcję. W powyższym przypadku jest to funkcja anonimowa, ale nic nie stoi na przeszkodzie, żeby przekazać też zwykłą funkcję.

Callbacki najczęściej są wykorzystywane w zapytaniach asynchronicznych (AJAX), kiedy podajemy funkcje, które mają się wykonać po otrzymaniu danych z zapytania HTTP lub po otrzymaniu błędu.

Problem pojawia się, gdy musimy zapytać o wiele różnych rzeczy asynchronicznie, a dopiero po otrzymaniu na wszystko odpowiedzi coś zrobić? Powiedzmy, że chcemy pobrać użytkownika, jego chaty i z najnowszego wiadomości z tego chatu, a później je wyświetlić. Używając callbacków przy zapytaniach asynchronicznych, wygląda to tak:

const wyswietlWiadomosci = function(username, password, callback){
   bazaDanych.pobierzUzytkownika(username, password, (error, user) => {
       if (error) {
           callback(error)
       } else {
         // ... logika
           bazaDanych.pobierzChaty(userId, (error, chaty) => {
               if (error) {
                   callback(error)
               } else {
                 // ... logika
                   bazaDanych.pobierzWiadomosci(chatId, (error) => {
                       if (error) {
                           callback(error);
                       } else {
                           // ... logika koncowa
                       }
                   })
               }
           })
       }
   })
};

wyswietlWiadomosci();

Jest to tak zwane callback hell. Im więcej zapytań asynchronicznych musimy zrobić naraz, tym większa choinka nam się tworzy. Z pomocą przychodzą tutaj promises.

Promises (obietnice) to obiekt udostępniony przez API, reprezentujący wartości, które będzie można wykorzystać w przyszłości. W przypadku zapytań asynchronicznych, promises pozwoli użyć wartości dopiero po skończeniu zapytania asynchronicznego. Przykład:

var nowaObietnica = new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", 'http://......');
    xhr.addEventListener('load', () => resolve(xhr.responseText));
    xhr.addEventListener('error', () => reject(new Error(xhr.statusText)));
    xhr.send();
});

nowaObietnica.then(
    result => console.log(result), // w obietnicy jest to przekazywane jako funkcja resolve
    error => console.error(error) // w obietnicy jest to przekazywane jako funkcja reject
);

Tworzymy obietnicę, a później ją wykorzystujemy, przekazując 2 funkcje, jedną na success, i jedną na error. Jak to nam pomaga w naszym callback hell? Można użyć funkcji, która zaczeka, aż wszystkie promisy będą skończone, i dopiero coś z nimi zrobi. Przykład:

function zapytanieAsynchroniczne(url) { 
    return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();
      xhr.open("GET", 'http://......');
      xhr.addEventListener('load', () => resolve(xhr.responseText));
      xhr.addEventListener('error', () => reject(new Error(xhr.statusText)));
      xhr.send();
    });
}
Promise.all([
    zapytanieAsynchroniczne('http://.../link/1'),
    zapytanieAsynchroniczne('http://.../link/2'),
    // ...
    zapytanieAsynchroniczne('http://.../link/100'),
  ])
  .then(response => {
    // response to array z odpowiedziamy ze wszystkich zapytan
    console.log(response);
  });

Czy jedno, czy sto zapytań, uruchamiamy funkcję dopiero po skończeniu wszystkich.

21. Czym jest Async/Await ?

Async/Await to inny, prostszy sposób zapisu funkcji asynchronicznej używającej obietnic (Promises). Sposób ten pozwala na prosta prace z obietnicami. Przykład:

async function funkcjaAsynchroniczna() {
  try {
    const wynik = await akcjaAsynchroniczna();
    console.log('Wynik - ' + wynik);
  } catch(error) {
    console.log('Error - ' + error);
  }
}

Tak wygląda teraz funkcja asynchroniczna. Przed deklaracją funkcji używamy słówka kluczowego async, a później, w momencie wykonywania jakiejś akcji asynchronicznej używamy słówka await. Mimo że jest to akcja asynchroniczna, to jednak już linię niżej możemy użyć jej wyniku. Dzieje się tak, ponieważ po dojściu do await funkcja jakby poczeka, aż zapytanie asynchroniczne się wykona, i dopiero po jego skończeniu pójdzie do kolejnych linii.

Użyliśmy też bloku try catch. Jest on potrzebny, ponieważ promisy mogą zwrócić błąd. W zapisie async/await nie dajemy funkcji, która ma się wykonać po otrzymaniu błędu, więc tę rolę przejmuje try catch. Argumentem catch() jest błąd, który otrzymujemy z obietnicy.

22. Jaka jest różnica między funkcjami synchronicznymi i asynchronicznymi?

Funkcje synchroniczne są wykonywane w momencie ich uruchomienia i interpreter czeka na ich skończenie, zanim uruchomi kolejne linie kodu. Funkcje synchroniczne blokują renderowanie UI aż do ich skończenia.

Funkcje asynchroniczne są wykonywane w momencie ich uruchomienia, po czym interpreter nie czeka na jej wykonanie, tylko zostaje uruchomiona kolejna linia kodu. Funkcja asynchroniczna może wykonać się bardzo szybko lub bardzo wolno, zależy od zadania. Dlatego interpreter nie czeka na jej wykonanie.

23. Co to jest SPA – Single Page Application. Czy jest to przyjazne dla SEO?

Single Page Application (SPA) to podejście do tworzenia stron internetowych. W tym podejściu strona jest załadowana tylko raz, po wejściu na nią, a później wszystko działa bez przeładowywania strony, używając głównie AJAX i zmian na drzewie DOM.

Istnieje wiele narzędzi, sprawiających, że wydajność SPA jest taka sama jak normalnego podejścia, czyli Multi Page Applications (MPA). Pierwsze załadowanie SPA zwykle jest wolniejsze niż MPA, za to nawigacja w MPA zwykle jest dłuższa niż w SPA.

Czy SPA jest przyjazne dla SEO? Jak najbardziej, istnieje wiele narzędzi ułatwiających prace z SEO i SPA, nie wspominając o tym, że od paru lat idea SPA jest mocno promowana, na przykład przez Google. Ich boty sprawdzające od dawna umieją radzić sobie ze stronami SPA. Podsumowując – nie ma problemów z SEO i SPA.

24. Czym jest PWA – Progresive Web App?

Progressive Web Application (PWA) to aplikacja frontendowa, która działa jak natywna aplikacja mobilna czy desktopowa. Może wyświetlać powiadomienia, działać offline oraz w tle, wykorzystuje funkcje urządzenia (aparat, nawigacja gestami, książka telefoniczna, karta graficzna itp.). Jednocześnie, w przeciwieństwie do aplikacji mobilnych czy natywnych, nie trzeba jej pobierać. Wszystkim zajmuje się przeglądarka.

25. Czym jest AJAX?

AJAX (Asynchronous JavaScript and XML, asynchroniczny JavaScript i XML) to technika tworzenia stron internetowych, gdzie interakcja z serwerem odbywa się bez przeładowania strony, w sposób asynchroniczny.

26. Czym jest JSON?

JSON (Java Script Object Notation) to prosty format wymiany danych. Używa JavaScriptowego zapisu obiektów. Przykład:

{
   "uczniowie":[
      {
         "id":0,
         "name":"Maciej"
      },
      {
         "id":1,
         "name":"Kamil"
      }
   ],
   "nauczyciele":[
      {
         "id":0,
         "name":"Jan"
      },
      {
         "id":1,
         "name":"Andrzej"
      }
   ]
}

27. Czym jest event loop? Jaka jest różnica między call stack i task queue?

Event loop to mechanizm, który sprawdza, czy stos wywołań (stack, call stack) jest pusty. Jeżeli jest, event loop bierze pierwszą rzecz z kolejki zadań (tast queue) i wrzuca ją do stosu wywołań. Brzmi skomplikowanie. Żeby to lepiej zrozumieć, musisz poznać, jak są wykonywane zadania w JSie i przeglądarce.

Kiedy uruchamiamy funkcję, trafia ona na stos wywołań(stack, call stack). Jeżeli ta funkcja uruchamia inną funkcję, ona również trafia na stos wywołań. W tym momencie stos ma w sobie 2 funkcje. Kiedy wychodzimy z funkcji, czy przez jej koniec, czy przez return, jest ona zdejmowana ze stosu wywołań.

Kolejna ważna rzecz, to API przeglądarki – Web API. Są to funkcje przeglądarki, z których możemy korzystać w naszym kodzie JS. Przykładami takich funkcji są np.funkcje powiązane z czasem (np. setTimeout czy setInterval) czy z zapytaniami XHR (XMLHttpRequest). Bardzo często są to funkcje asynchroniczne. Po ich uruchomieniu callback tych funkcji jest umieszczany w kolejce zadań (task queue).

Kolejka zadań (task queue) to kolejka, w której umieszczane są wszystkie callbacki metod asynchronicznych. Jest to kolejka FIFO – First In First Out, czyli pierwszy callback, który trafił do kolejki, będzie pierwszym, który z niej wyjdzie. Ważna sprawa – nic nie może wyjść z kolejki zadań, jeżeli stos wywołań nie jest pusty. Gdy event loop trafia tuta i stos wywołań jest pusty, zostaje zabrana jedna, tylko jedna rzecz z kolejki zadań i przeniesiona zostaje do stosu wywołań.

Istnieje jeszcze coś takiego jak kolejka renderowania (ang. Render queue). Jest to kolejka, w której umieszczane są wszystkie operacje, które zmieniają coś na stronie przed kolejnym wyrenderowaniem strony. Zmiana stylu, przeliczenie wysokości, czy dynamiczne dodanie elementu do drzewa DOM – tutaj jest kolejka, która za to odpowiada. W przeciwieństwie do kolejki zadań ta kolejka jest wykonywana aż do końca, aż będzie pusta, oraz ma wyższy priorytet niż kolejka zadań.

Cały proces można sprowadzić do tych kroków:

  1. Wykonaj wszystko ze stosu wywołań.
  2. Sprawdź, czy jest coś w kolejce renderowania. Jeżeli jest – Wykonaj wszystko.
  3. Sprawdź, czy jest coś w kolejce zadań. Jeżeli jest, sprawdź, czy stos wywołań jest pusty. Jeżeli stos wywołań jest pusty, weź JEDNĄ rzecz z kolejki zadań i przenieś ją do stosu wywołań.
  4. Idź do punktu 1.

28. Jak działa funkcje czasu w JavaScript (setTimeout, setInterval)?

setTimeout to funkcja, która wykonuje kod po określonym czasie. Jako pierwszy argument przyjmuje funkcję, którą wykona po wyznaczonym czasie. Drugi argument to czas opóźnienia wykonania funkcji w milisekundach.

setTimeout(function(){
    console.log('wykonanie po sekundzie');
}, 1000);

setInterval to funkcja, która wykonuje kod co określony czas, zaczynając po pierwszym okresie. Pierwszy argument to funkcja, która ma być wykonywana cyklicznie. Drugi argument to czas, co ile ma być wykonywana funkcja, podany w milisekundach. setInterval zostawia też obiekt, dzięki któremu możemy w dowolnym momencie wyłączyć powtarzanie.

var interwal = setInterval(function () {
    console.log('wykonanie co sekunde');
}, 1000);




setTimeout(function(){
    clearInterval(interwal);
}, 10000);

29. Jaka jest różnica między obiektami mutable i immutable?

Obiekty mutable to obiekty, które mogą być zmienione po zadeklarowaniu i przypisaniu wartości. Przykładem natywnych obiektów mutable są np. obiekty, funkcje, tablice.

Obiekty immutable to obiekty, które nie mogą być zmienione po zadeklarowaniu i przypisaniu. Przykładem natywnych obiektów immutable są np. number i string. Za każdym razem, gdy zmieniamy ich wartość, tak naprawdę przypisujemy nowy obiekt do zmiennej, a nie modyfikujemy stary.

30. Co to jest „use strict”;?

Wyrażenie „use strict” pozwala przełączyć parser JavaScriptu w tak zwany strict mode. Sprawia to, że parser JS jest o wiele bardziej rygorystyczny i pokazuje błędy, które bez „use strict” by ignorował. Przykład:

'use strict';

zmienna = 'hello'; // użycie niezadeklarowanej zmiennej

Używanie strict mode jest bardzo zalecane, gdyż pozwala pisać lepszy kod i unikać błędów.

31. Co to jest transpilacja? Dlaczego się to stosuje? Jakie są plusy i minusy? Jakie są przykłady języków transpilowanych?

Transpilacja to proces, podczas którego zamieniasz kod źródłowy jednego języka programowania w kod źródłowy innego języka programowania. W przypadku JSa bardzo często zamieniany jest język z grupy kompilowanych do JSa, na przykład TypeScript czy CoffeScript. Przykłady:

TypeScript:

"use strict";

function wypisz( wiadomosc : string ) {
    console.log(wiadomosc);
}

wypisz("TypeScript!");

CoffeeScript:

"use strict"

wypisz(wiadomosc) =>
    console.log wiadomosc

wypisz "CoffeeScript!"

Powyższe przykłady wygenerują ten sam kod w JavaScript:

"use strict";

function wypisz( wiadomosc) {
    console.log(wiadomosc);
}

wypisz("Stary dobry JavaScript!");

Transpilatorów, a w zasadzie języków kompilowanych, używa się głównie z trzech powodów:

  • Główny powód – udostępniają funkcje, jakich nie ma w JSie, np. typowanie
  • Średnio ważny – czasami zwiększają wydajność kodu.
  • Mniej wazny – pozwalają pisać w JSie w znajomej składni, na przykład CoffeeScript przypadnie do gustu Pythonowcom, TypeScript może się podobać dla ludzi zaznajomionych z jakimś językiem obiektowym, a użytkownicy Clojure polubią ClojureScript.

32. Jak stworzyć swoją przestrzeń nazw w JS?

Przestrzeń nazw to miejsce, gdzie można zawrzeć zmienne i funkcje pod unikalną nazwą, oddzielając je od reszty kodu. Zwykle przestrzeń nazw stosuje się, aby zapobiec konfliktom nazw zmiennych i funkcji. Jest wiele sposobów na tworzenie przestrzeni nazw. Poniżej przykładowe trzy:

Najprościej jest użyć clojure.

Można stworzyć funkcję otaczającą cały kod:

(function() { 
  var imie = 'Andrzej';
  console.log(imie); // Andrzej
})();

console.log(imie); // ReferenceError: imie is not defined

Można też stworzyć obiekt, w którym będzie trzymany cały kod:

var aplikacja = {}
 
aplikacja.imie = 'Andrzej';
 
aplikacja.przedstawienie = function() {
    console.log('Witaj, nazywam sie ' + aplikacja.imie);  
}

aplikacja.przedstawienie();

To tylko trzy sposoby, ale jest ich o wiele więcej.

33. Funkcje map, filter i reduce – jak działają?

map tworzy nową tablicę z rezultatem wykonania wskazanej funkcji na każdym elemencie podanej tablicy. Przykład:

var liczby = [1, 2, 3, 4];
 
var podwojone = liczby.map(function(x){
  return x*2;
});

console.log(podwojone); // [2, 4, 6, 8]

filter tworzy nową tablicę z rezultatem wykonania wskazanej funkcji z porównaniem na każdym elemencie podanej tablicy. Nowa tablica może mieć tyle samo elementów, co stara tablica, ale może mieć też mniej lub wcale, zależnie od wyników porównania. Przykład:

var liczby = [1, 2, 3, 4];
 

var wieksze = liczby.filter(function(x) {
  return x > 2;
});

console.log(wieksze); // [3, 4]

reduce tworzy nową zmienną z rezultatem wykonania funkcji redukującej na każdym kolejnym elemencie podanej tablicy. Chodzi o to, aby z całej tablicy mieć jedną zmienną o dowolnej wartości, np. tablica, numer czy string. zawsze podajemy również wartość początkową (w przykładzie poniżej jest to zero) zmiennej wynikowej. Przykład:

var liczby = [1, 2, 3, 4];
 
var suma = liczby.reduce(function (suma, x) {
  return suma + x;
}, 0);

console.log(suma); // 10

34. Jaka jest różnica między .forEach i .map. Kiedy ich użyjesz?

forEach wykonuje wskazaną funkcję jeden raz na każdym elemencie podanej tablicy. forEach może modyfikować tablicę, na której działa. Przykład:

var tablica = [1,2,3,4,5];
tablica.forEach((element, index) => {
    return tablica[index] = element * 2;
});

map tworzy nową tablicę z rezultatem wykonania wskazanej funkcji na każdym elemencie podanej tablicy. Przykład:

var tablica = [1,2,3,4,5];
var podwojonaTablica = tablica.map(element=> {
    return element* 2;
});

35. Jak jest nowy sposób tworzenia funkcji w ES6? Czym się różni od starego?

Nowy sposób tworzenia funkcji w ES6 to tak zwana fat arrow lub inaczej funkcje strzałkowe. Funkcje są tworzone przy użyciu operatora =>.

//Zwykły JS, zwykła funkcja
var podwojenie = function(numer){
	return numer * 2;
}
console.log(podwojenie(2))
 
//to samo uzywając arrow function
var podwojenie = numer => numer * 2;
console.log(podwojenie(4))

Proste, prawda? Nie ma nawet nawiasów, które sa opcjonalne przy jednym argumencie w funkcji. Jeżeli chcemy mieć więcej argumentów, musimy je dodać:

var suma = (numer1, numer2) => numer1 + numer2;
console.log(suma(1,2))

Jeżeli chcemy, żeby nasza funkcja robiła cos więcej, niż zwracanie elementu, musimy dodać również nawiasy klamrowe:

var wypiszSume = (numer1, numer2, numer3) => {
  let zmienna = numer1 + numer2 + numer3;
  console.log(zmienna);
};
wypiszSume(1,2,3);

Funkcje tworzone przy użyciu fat arrow nie tworzy ona swojego this, a bierze go od rodzica. Inaczej mówiąc, odpowiada zwykłej funkcji z dodatkiem .bind(this).

36. Jak współdzielić kod JS między plikami? Co to jest wzorzec module?

Wzorzec modułu to wzorzec programowania, w którym tworzymy oddzielną przestrzeń nazw/scope. Robi się to po to, aby uniknąć konfliktów nazw, zablokować wycieki danych do pamięci globalnej (czyli spowolnienie strony) czy stworzyć prywatne zmienne z publicznymi funkcjami.

Moduł można stworzyć na kilka sposobów. Najpopularniejsze jest stworzenie nowego obiektu i przypisanie wszystkich zmiennych i funkcji do niego:

var MODUL= {}
 
MODUL.imie = 'Andrzej';
 
MODUL.przedstawienie = function() {
    console.log('Witaj, nazywam sie ' + aplikacja.imie);  
}

MODUL.przedstawienie();

37. Co to jest Gulp? Do czego służy?

Gulp jest task runnerem. Oznacza to, że możemy uruchamiać nim raz zdefiniowane zadania, takie jak np. zbudowanie wersji produkcyjnej, wrzucenie zmian na serwer testowy czy skopiowanie plików. Gulp pozwala automatyzować wszystkie zadania, które są nudne, długie i powtarzalne.

Może też stworzyć serwer developerski, który ułatwi nam prace nad aplikacją, poprzez np. sprawdzanie składni, formatowanie czy auto odświeżanie strony po zapisie pliku.

Zadania w Gulpie są tworzone w specjalnym pliku gulpfile.js w języku JavaScript.

38. Co to jest webpack? Do czego służy?

Webpack to budler, czyli program do tworzenia paczek z kodem z naszego kodu, nad którym pracujemy. Często w projekcie mamy setki plików. Zamiast wysyłać to wszystko na serwer produkcyjny oddzielnie, możemy stworzyć jedną ogromną paczkę z całym kodem. Żeby nie robić tego ręcznie, powstał Webpack. Po drodze tej zmiany Webpack pozwala nam również zmieniać pliki, np.usuwać komentarze, podmieniać konkretne zmienne na inne czy transpilować kod.

39. Co to jest NPM? Do czego służy?

NPM (Node Package Manager) jest domyślnym menadżerem pakietów dla Node.js. Najważniejsza cecha NPMa to możliwość ściągania bibliotek i frameworków z głównego rejestru (np. Angular, React, Gulp itp.). Ma też wiele innych opcji, ale te warto poznać z czasem.

Podstawowym plikiem NPM-a jest package.json. W pliku tym są zawarte podstawowe informacje o projekcie oraz, co jest dla nas najważniejsze, lista wszystkich zależności i zależności developerskich do projektu. Jest to lista wszystkich bibliotek, frameworków i paczek które mamy w projekcie. Istnieje po to, aby nie musieć po ściągnięciu projektu na nowo ręcznie wpisywać nazw i ściągać wszystkich paczek z osobna. Po użyciu komendy npm install NPM sam instaluje wszystkie zależności zapisane w pliku package.json.

Plusem będzie, gdy opowiesz czym jest i jak działa Yarn.

40. Jakie znasz techniki i narzędzia do poprawy jakości kodu?

Pytanie otwarte, na które nie ma jednej odpowiedzi. Warto jednak znać najpopularniejsze narzędzia do poprawy kodu, takie jak:

  • Lintery, czyli programy sprawdzające składnie np. TSLint, Eslint JSLint
  • Continuous integration (CI), czyli narzędzia pozwalające na ciągłą integrację najnowszego kodu, uruchamianie testów i buildu po każdym commicie, i wiele, wiele więcej.
  • Code Review, czyli ktoś musi przejrzeć i zaakceptować twoje zmiany przed dodaniem ich do głównego kodu.
  • Znajomość dobrych praktyk programowania w JS, takich jak SOLID, DRY czy KISS.
  • Używanie dobrych Style Guide, na przykład Airbnb czy Google Style Guide
  • Używanie narzędzi takich jak Prettier, do automatycznego formatowania np. plików HTML.
  • Typowanie zmiennych
  • Testowanie kodu

Oczywiście to nie wszystko, ale już całkiem pokaźny zbiór. Pamiętaj jednak, że poza znajomością nazw, warto zagłębić się w temat i poznać, o co chodzi. Na rozmowie często prosi się o wyjaśnienie, jak któraś technika działa czy czym jest np. SOLID.

41. Jakie znasz przydatne narzędzia lub biblioteki?

Pytanie otwarte, na które nie ma jednej odpowiedzi. Można przedstawić wiele rzeczy, np.

  • Znane popularne frameworki lub biblioteki JS, takie jak np. jQuery, Angular, React.
  • Popularne i bardzo przydatne IDE, np. Visual Studio Code, WebStorm, Atom czy Sublime Text.
  • Znane przydatne rozszerzenia do Visual Studio Code np. Prettier czy snippety do wybranego frameworka, czy biblioteki.
  • Frameworki i biblioteki pozwalające tworzyć aplikacje natywne czy hybrydowe.
  • Bundlery, np. Webpack czy Browserify
  • Task runnery np. Gulp czy Grunt
  • System zarządzania wersjami kodu np. Git czy SVN

Jest tego więcej, więc śmiało z inwencją twórcza. Jeżeli cokolwiek ci pomaga w pracy programisty – można to tutaj dać.

42. W jaki sposób i jakimi narzędziami debugujesz projekty z użyciem JS?

Pytanie otwarte, na które nie ma jednej odpowiedzi. Najpopularniejsze sposoby debugowania kodu JS to:

  • debugger wbudowany w przeglądarkę
  • debugger wbudowany w IDE
  • Do debugowania zapytań – Postman
  • Do debugowania HTMLa – inspektor wbudowany w przeglądarki

43. Czym się różni framework od biblioteki.

Framework to szkielet do budowania aplikacji. Posiada wiele przydatnych funkcjonalności, np. funkcje do zapytań HTTP, renderowanie widoku czy optymalizacja działania frameworka. Framework to gotowa instrukcja JAK pisać aplikację.

Biblioteka to kod, który koncentruje się na rozwiązaniu jednego problemu i zaoferowaniu API. Biblioteka to tylko kawałek aplikacji odpowiada za jedną rzecz.

44. Po co w ogóle jest JavaScript na stronie? Co nam daje?

JavaScript pozwala na interakcje użytkownika ze stroną. Jest to definicja podstawowa. JS bardzo się rozbudował przez ostatnie lata, więc teraz poza interaktywnością oferuje o wiele więcej, na przykład dynamiczne doładowywanie danych i zmianę zawartości strony, animacje czy walidacja danych w formularzu jeszcze przed wysłaniem go na serwer.

Strony bez JSa są statyczne. Oczywiście można wypełnić formularze i wysłać je do backendu, ale nic poza tym. Strony wyświetlają tylko statyczne informacje czy formularze i nie reagują na większość akcji użytkownika. JS to zmienia.

Podsumowanie

W końcu koniec. Gratuluję, udało Ci się, wszystkie czterdzieści i cztery pytania za Tobą. Jest tutaj sporo wiedzy, a większość z powyższych punktów jest dobrym tematem na osobny wpis. I tak się zapewne stanie, prawdopodobnie za jakiś czas dokładnie opiszę każde powyższe zagadnienie, ale na razie chciałem stworzyć jedno miejsce, gdzie znajdziesz najpopularniejsze pytania do JSa. Jedno miejsce, które otwierasz, jeżeli szukasz teorii na rozmowę kwalifikacyjną.

Jeżeli mi się udało, jeżeli ten wpis Ci pomógł – daj znać. Będzie mi bardzo miło, wiedząc, że komuś pomogłem 🙂

ZOSTAW ODPOWIEDŹ

Please enter your comment!
Please enter your name here