Node.js Tworzenie własnych modułów

Już co nieco dowiedzieliśmy się o modułach z poprzedniego wpisu „Node.js #03 – Wprowadzenie do modułów„. Teraz czas przejść do bardziej praktycznej części jeśli chodzi o moduły czyli stworzymy własny moduł. Nie będzie on zbyt wyszukany jednak pozwoli na zrozumienie postaw.

Zanim zajmiemy się samymi modułami musimy mieć podstawową wiedzę na temat funkcji oraz zakresu globalnego w JavaScript. Jest to związane bezpośrednio z tym jak są zbudowane moduły oraz ich sposobem działania. Nie martwcie się jeśli nigdy nie słyszeliście o funkcjach czy też zakresach, gdyż postaram się wam to wyjaśnić w jak najprzystępniejszy sposób. Do dzieła…

Funkcje w JavaScript

Funkcje możecie spotkać w każdym języku programowania przy czym w językach funkcyjnych jakim jest Java Script są obywatelami pierwszej kategorii. Co to właściwie oznacza ? Otóż język wymusza niemal ciągłe używanie funkcji, ale także daje możliwość przypisywania ich do zmiennych, przekazywania jako parametr, dodawanie jako element tablicy i wiele więcej. Ważne, abyś zapamiętał(a), że funkcje są fajne chyba że trafisz do callback hel (o tym napiszę osobny wpis, gdyż jest to dość ciekawy problem).

Zapewne zastanawiasz się czym są funkcje, i do czego służą ? Funkcje są blokami kodu, które pozwalają nam na wielokrotne wykonanie tego kodu bez potrzeby pisania go ponownie. Kto bowiem by chciał pisać za każdym razem pisać mechanizm wyliczania wartości faktury, gdy będzie potrzebował tej wartości w kodzie ? Drugą ważną cechą funkcji jest porządkowanie kodu, bowiem to dzięki nim w jakiś sposób staramy się usystematyzować kod naszej aplikacji.

Definiowanie (tworzenie) funkcji

Zacznijmy od najprostszego przykładu funkcji, a później będziemy go rozbudowywać.

// definujemy funkcję
function hello() {

    console.log('Hello');
}

// wywołujemy (uruchamiamy) funkcję
hello();

Powyższy kod definiuje funkcję hello oraz ją wywołuje, zobaczmy krok po kroku jak dojść do takiego kodu.

Zaczynamy od zdefiniowania funkcji za pomocą słowa kluczowego function. Po słowie kluczowym podajemy nazwę w powyższym przykładzie nazwa funkcji to hello, czyli mamy

function hello

Teraz dodajemy nawiasy klamrowe, które służą do zdefiniowania listy parametrów jakie można przekazać do funkcji. W tym przypadku nie przekazujemy jeszcze żadnego parametru.

function hello()

Ostatni element to ciało funkcji, czyli mówiąc prościej kod który ma być wykonywany za każdym razem, gdy funkcja będzie wywoływana. Ciało funkcji musi zostać zawarte w nawiasach klamrowych.

function hello() {

}

W ten sposób doszliśmy do pustej funkcji hello, do której wystarczy dodać proste wyświetlanie komunikatu w konsoli i mamy naszą pierwszą funkcje w JavaScript.

function hello() {

    console.log('Hello');
}

Teraz tylko wystarczy ją uruchomić co robimy podając jej nazwę oraz listę parametrów w nawiasach klamrowych. W tym przypadku nie podaliśmy żadnych parametrów więc wywołanie wygląda następująco:

hello();

Proste prawda? To skomplikujmy to troszkę 😉

Co byś powiedział(a), gdybym powiedział Ci że nazwa w funkcji jest zbędna? Pewnie zapytał(a) byś jak w takim razie wywoływać takie funkcje. I to by było bardzo dobre pytanie !!! Otóż w JavaScript mamy do dyspozycji funkcje anonimowe, nazywają się one tak ze względu na brak nazwy, powyższy przykład dla takiej funkcji wyglądał by następująco:

function() {

    console.log('Hello');
}

Tylko jak taki cud natury uruchomić ? Właściwie opcje są dwie, pierwsza to dodać na końcu ()

function() {

    console.log('Hello');
}();

Prawda że piękne, dobra bżyyyyydal, ale to jest nieistotne gdyż częściej spotkasz się z przypisaniem takiej funkcji anonimowej do zmiennej.

var hello = function() {

    console.log('Hello');
};

Teraz do zmiennej hello jest przypisana funkcja i możemy ją wywoływać tak jak to robiliśmy poprzednio:

hello();

Przypisywanie funkcji do zmiennych jest o tyle fajnym rozwiązaniem że pozwala na przekazywanie funkcji jako parametr o czym opowiem Ci za chwilę przy części omawiającej przekazywanie parametrów do funkcji.

Przekazywanie parametrów do funkcji

Możesz spytać o co chodzi z tymi parametrami? I słusznie, parametry pozwalają nam w sposób jawny przekazać jakieś wartości do funkcji, mogą to być także inne funkcje o czym zaraz się przekonasz 😉

Mamy naszą funkcję hello i chcemy, aby w konsoli wyświetlał się komunikat np. Hello Marcin zamiast samego Hello. Do tego celu parametry nadają się idealnie, dokonujemy modyfikacji naszej funkcji, dodając parametr name w nawiasach klamrowych.

function hello( name )

Przy tak zdefiniowanej funkcji w jej ciele uzyskujemy dostęp do zmiennej name, którą dodaliśmy do listy parametrów. Więc możemy dokleić ją do komunikatu wyświetlanego w konsoli.

function hello( name ) {

    console.log('Hello ' + name);
}

Jako że zmieniła się lista parametrów naszej funkcji to jej wywołanie także się zmieni na:

hello('Marcin');
// zostanie wyświetlone w konsoli: Hello Marcin

Wywołanie funkcji zmieniło się niewiele, jedyna różnica to przekazanie parametru do funkcji. Ciekawiej się robi, gdy funkcja pozwala na przekazanie innej funkcji jako parametr.

function hello( name, callback ) {

    console.log('Hello ' + name);

    // wywołanie funkcji przekazanej jako parametr
    callback();
}

Do funkcji hello dodałem kolejny parametr callback. Parametr ten powinien być funkcją, która zostanie wywołana po wyświetleniu komunikatu w konsoli. Wywołać tak zdefiniowaną funkcję możemy na dwa sposoby w obu przypadka potrzebna będzie funkcja anonimowa.


hello('Marcin', function() {

    console.log('Komunikach z funkcji anonimowej');
});

lub


var userMessage = function() {

    console.log('Komunikach z funkcji anonimowej');
};

hello('Marcin', userMessage);

W obu powyższych przypadkach zostanie wyświetlony komunikat w konsoli:

Hello Marcin
Komunikach z funkcji anonimowej

Zwracanie wartości z funkcji

Oprócz tego że do funkcji możemy przekazywać parametry to także z funkcji możemy zwracać wartości. Zwracamy wartości z funkcji wykorzystując słowo kluczowe return, zmodyfikujemy naszą funkcję hello, aby zwracała komunikat zamiast go wyświetlać.

function hello( name ) {

    return 'Hello ' + name;
}

Modyfikacja niewielka, a od tego momentu funkcja zwraca treść komunikatu co z nim zrobimy jest uzależnione tylko od nas. Czy to będzie przypisanie do zmiennej, czy też wyświetlenie w konsoli.

// przypisanie do zmiennej
var message = hello('Marcin');

// wyświetlenie w konsoli
console.log( hello('Marcin') );

Ostatnia kwestia to co można z funkcji zwracać, właściwie wszystko. Zwracaną wartością może być inna funkcja, obiekt, tablica, ciąg znaków itd.

Zakresy w Node.js

Programując czy to w JavaScript czy też innym języku zetkniemy się z pojęciem zakresu, jednak różne języki mogą mieć inne podejście do zakresów. Zacznijmy od tego czym owy zakres jest, wyjaśnię to na przykładzie. Wyobraź sobie mieszkanie z kilkoma pomieszczeniami, będąc w pokoju masz w nim jakieś rzeczy i masz do nich dostęp w ramach zakresu pokoju. Jednak nie masz dostępu do rzeczy znajdujących się w pokoju obok, bo jest poza twoim bieżącym zakresem. Ważne jest jeszcze to, że w ramach każdego pokoju masz dostęp do wszystkiego co jest w zakresie mieszkania np. drzwi wyjściowe.

To teraz nieco bardziej praktyczne podejście, gdyby nie udało mi się wyjaśnić dość przystępnie zakresów na przykładzie mieszkania 😉

function marcin() {

    var name = 'Marcin';
    console.log(name);
}

function piotr() {

    var name = 'Piotr';
    console.log(name);
}

marcin(); // wyświetli: Marcin
piotr(); // wyświetli: Piotr

Jak narazie mam nadzieję że kod jest jasny. Mamy dwie funkcje, które w swoich ciałach mają zmienne o takiej samej nazwie jednak ich modyfikacja nie ma wpływu na żadną funkcję. Jest to spowodowane tym że zmienne znajdują się poza zakresem funkcji, zmieńmy to. W tym celu dodamy zmienną company znajdującą się w zakresie obu funkcji.


var company = "Company Name S.A."

function marcin() {

    var name = 'Marcin';
    console.log(name);
    console.log(company);
}

function piotr() {

    var name = 'Piotr';
    console.log(name);
    console.log(company);
}

marcin(); // wyświetli: Marcin Company Name S.A.
piotr(); // wyświetli: Piotr Company Name S.A.

W takim przypadku istnieje ryzyko, że jakaś funkcja zmodyfikuje wartość zmiennej co wpływa na pozostałe funkcje wykorzystujące tę zmienną.


var company = "Company Name S.A."

function marcin() {

    // modyfikujemy nazwę firmy
    company = "Other Company S.A.";

    var name = 'Marcin';
    console.log(name);
    console.log(company);
}

function piotr() {

    var name = 'Piotr';
    console.log(name);
    console.log(company);
}

marcin(); // wyświetli: Marcin Other Company S.A.
piotr(); // wyświetli: Piotr Other Company S.A.

Pomimo że wartość zmiennej została zmieniona tylko w jednaj funkcji w kolejnej już obowiązuje zmieniona wartość. Wszystkie pokazane w przykładach zmienne są zmiennymi tak zwanymi lokalnymi, oznacza to tyle że są dostępne w ramach swoich zakresów. Jeśli chcemy, aby jakaś zmienna była dostępna wszędzie czyli była zmienną globalną wystarczy pozbawić jej słowa kluczowego var.


function marcin() {

    // ustawiamy wartość zmiennej company, 
    // która będzie dostępna we wszystkich zakresach
    company = "Other Company S.A.";

    var name = 'Marcin';
    console.log(name);
    console.log(company);
}

function piotr() {

    var name = 'Piotr';
    console.log(name);
    console.log(company);
}

marcin(); // wyświetli: Marcin Other Company S.A.
piotr(); // wyświetli: Piotr Other Company S.A.

W powyższym przykładzie została usunięta definicja zmiennej company, oraz w funkcji marcin usunięto var przed company. Co spowodowało, że ów zmienna stała się dostępna wszędzie w pliku oraz tam gdzie zostanie dołączony plik z tym kodem.

Zasady dostępności do zmiennych w JavaScript są tematem złożonym i zapewne spotkacie się z wieloma przypadkami, gdzie zmienne zachowają się inaczej niż byście przypuszczali. Mam tylko prośbę zapamiętajcie że nie używamy zmiennych globalnych.

Tworzymy moduł

Skoro liznęliśmy podstaw, które są potrzebne do tworzenia własnych modułów to czas na najciekawsze czyli piszemy własny moduł. Zacznijmy od tego że w Node.js każdy plik traktowany jest jako moduł, a jego wnętrze jest odseparowane od pozostałych. Czyli mając pliki:

app.js – kod aplikacji
custom_modules/hello.js – moduł

Moduł w tym momencie jedyne co robi to definiuje funkcję hello

// plik custom_modules/hello.js

function hello(name) {
    
    console.log('Hello ' + name);
}

W kodzie aplikacji importujemy moduł i próbujemy użyć funkcji hello

// plik app.js

// import modułu
require('./custom_modules/hello.js');

// próba wywołania funkcji modułu
hello('Marcin');

Oczywiście powyższa próba skazana jest na niepowodzenie, jest to spowodowane tym, że nie mamy dostępu do funkcji hello. Brak dostępu wynika z faktu, że aby coś było widoczne poza modułem konieczne jest wyeksportowanie tego z modułu. Eksport odbywa się przez przypisanie funkcji, zmiennej, obiektu, czegokolwiek do specjalnej zmiennej exports. W związku z czym zmodyfikujmy nasz kod modułu:

// plik custom_modules/hello.js

// eksportujemy funkcję która będzie dostępna pod zmienną hello
exports.hello = function (name) {
    
    console.log('Hello ' + name);
};

Teraz musimy odpowiednio obsłużyć eksportowane elementy z modułu w kodzie aplikacji.

// plik app.js

// import modułu i przypisanie eksportowanych elementów 
// do zmiennej myModule
var myModule = require('./custom_modules/hello.js');

myModule.hello('Marcin');

Pojawiło się kilka nowych elementów, mianowicie import modułu został przypisany do zmiennej myModule. Jeśli się nad tym zastanowić ma to sens, wywołujemy funkcję require, która zwraca zmienną exports do której to przypisywaliśmy funkcje, obiekty itd. Na tak zdefiniowanej zmiennej możemy wywołać wyeksportowaną funkcję hello.

Tyle na dziś wystarczy, mamy moduł który działa. Może nie robi niczego ciekawego jednak mamy podstawową wiedzę, która zapewne będzie wymagała jeszcze uściślenia, ale w programowaniu ciągle trzeba się rozwijać i poszerzać wiedzę.

„Tylko ten nie popełnia błędów, kto nic nie robi” – Napoleon Bonaparte

Close