Przestrzenie nazw i moduły w ES6

O zaletach korzystania z przestrzeni nazw w takich językach jak C++, Java czy PHP chyba nie trzeba nikogo przekonywać. Właściwie w dużych projektach, zwłaszcza w takich co silnie wykorzystują zewnętrzne biblioteki odpowiednia separacja kodu jest niezbędna. W tym artykule będę chciał przedstawić jak się sprawy mają w JavaScript historycznie jak i w dobie standardu ECMAScript 6 (ES6).

Dla osób przyzwyczajonych do technologii C++, Java czy PHP może być pewnym szokiem, że przestrzenie nazw jako takie w JavaScript nie istnieją. Zamiast tego wykorzystuje się tu koncepcje modułów, chociażby za sprawą ES6, który je ustandaryzował. Przedtem obowiązywały standard AMD z jego implementacją require.js dla rozwiązań po stronie klienta oraz CommonJS dla środowiska Node.Js. Był jeszcze standard UMD, który próbował połączyć obydwa te rozwiązania. To co stanowiło siłę tych technologii to ograniczenie modułów do jednego pliku, automatycznie załączanie modułów na podstawie ich wzajemnych zależności czy możliwość wczytywania poszczególnych modułów z różnych bibliotek bez ładowania całości.

Przykład z http://papoldesign.pl/edukacja/requirejs-czym-amd-api-podstawy-uzycia-loadera/

requirejs.config({
paths: {
'jquery': 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min'
}
});

require(['jquery'], function ($) {

require(["one", "two", "three"], function (one, two, three) {
$('h1').html(three - one + two);
});
});

gdzie na początku ustalamy ścieżkę do biblioteki jQuery, następnie deklarujemy moduł wstrzykując jQuery w zmiennej “$” oraz dociągamy kolejne moduły (one.js, two.js ,three.js).

A jak to wygląda obecnie, w dobie ECMAScript 6?
Właściwie podobnie, moduły znajdują się w pliku js z tą różnicą, że jego wczytanie odbywa się przez słowo kluczowe import a deklaracja poprzez użycie export. Możemy eskportować zarówno funkcje, zmienne, stałe jak i klasy.

//plik pi.js
export let PI = () => {
return 3.14;
};

//plik main.js
import * as math from '~/static/pi.js'
console.log(math.PI());

To o czym warto pamiętać w kontekście modułów z ES6 to, że:

  • domyślnie stosują tryb strict,
  • są one wczytywane asynchronicznie,
  • zmienne lokalne widoczne są jedynie w obrębie modułu,
  • są wywoływane tylko raz,
  • można je też zadeklarować poprzez znacznik “<script type=module>“.

Pewnym problemem może by wczytywanie dynamicznie modułów, powszechnie stosowanie rozwiązanie sprowadza się w takim przypadku do wykorzystania async/await

sync function load() {
const obj = await import("./functions.js");
module.smallText();
module.bigText();
}

const button = document.querySelector("button");
button.addEventListener("click", async () => {
const obj = await import("./functions.js");
module.smallText();
module.bigText();
});

Muszę przyznać, że ES6 naprawdę wymiata. Więcej pod https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules