1. Global scope
Zdefiniuj funkcję w pliku A - w pliku B możesz ją wywołać. Ważne tylko, aby plik A był w kodzie HTML przez plikiem B (W ten sposób korzysta się z bibliotek ściąganych na przykład z CDN-ów)
+ Zerowy próg wejścia
- Można zaśmiecić global scope'a
- Kod ciężki w utrzymaniu / rozwijaniu
2. Moduły ES6 - przeglądarka
Dodaj do tagu script atrybut type="module". Moduły zaczynają działać.
+ Niski próg wejścia
+ Całkiem przyjemnie można zarządzać strukturą projektu
- Niewielkie wsparcie
3. Moduły ES6 - node + module bunlder
Lokalnie używasz bundlera aby połączyć moduły w jeden plik
+ W nodzie można zbudować potężne środowisko developerskie (hot-reloading, minifikacja zasobów, transpilacja ES6+ / JSX / vue / TS /... , testy, linting, itd...)
+ W prosty sposób można zoptymalizować plik wyjściowy
+ Dowolne* wsparcie przeglądarek, przy możliwości pisania nowoczesnego kodu.
- Nieograniczone możliwości - potrzeba sporej wiedzy, żeby to wszystko "ogarnąć"
Zachęcam do zapoznania się na przykład z webpackiem lub rollupem (gdzie podstawowy bundling wykonasz jedną komendą)
Są jeszcze natywne moduły node'a (commonJS), ale wypadają blado przy ES6
4. AMD
Asynchronous Module Definition - w sumie to już martwe, ale rozwiązują problem. (requireJS - chyba najpopularniejszy module loader) Zdecydowanie odradzam.
Jeśli zaczynasz naukę JS, polecam sposób pierwszy. Na chwilę rozwiązuje problem i sam przekonasz się, jakie są jego wady.
Obecnie świat webdevu stoi na sposobie numer 3 i - jeśli się tym interesujesz - tam bym dążył na Twoim miejscu : )