Writing Modular JavaScript With AMD, CommonJS & ES Harmony

Tweet

Modularność The Importance Of Decoupling Your Application

Gdy mówimy, że aplikacja jest modularna, generalnie mamy na myśli, że składa się z zestawu wysoce odsprzężonych, odrębnych kawałków funkcjonalności przechowywanych w modułach. Jak zapewne wiesz, luźne sprzężenie ułatwia utrzymanie aplikacji poprzez usuwanie zależności tam, gdzie jest to możliwe. Kiedy jest to zaimplementowane efektywnie, dość łatwo jest zobaczyć jak zmiany w jednej części systemu mogą wpłynąć na inną.

W przeciwieństwie do niektórych bardziej tradycyjnych języków programowania, jednakże, obecna iteracja JavaScript (ECMA-262) nie dostarcza programistom środków do importowania takich modułów kodu w czysty, zorganizowany sposób. Jest to jeden z problemów ze specyfikacjami, które nie wymagały wielkich przemyśleń aż do ostatnich lat, gdzie potrzeba bardziej zorganizowanych aplikacji JavaScript stała się oczywista.

Zamiast tego, deweloperzy obecnie są pozostawieni, aby oprzeć się na odmianach modułu lub wzorców dosłownych obiektów. Z wieloma z nich, skrypty modułów są nawleczone razem w DOM z przestrzeniami nazw opisanymi przez pojedynczy obiekt globalny, gdzie nadal jest możliwe, aby ponieść kolizje nazewnictwa w twojej architekturze. Nie ma też czystego sposobu na zarządzanie zależnościami bez ręcznego wysiłku lub narzędzi firm trzecich.

Podczas gdy natywne rozwiązania tych problemów pojawią się w ES Harmony, dobrą wiadomością jest to, że pisanie modularnego JavaScriptu nigdy nie było łatwiejsze i można zacząć to robić już dziś.

W tym artykule przyjrzymy się trzem formatom pisania modularnego JavaScriptu: AMD, CommonJS i propozycjom dla następnej wersji JavaScriptu, Harmony.

Prelude A Note On Script Loaders

Trudno jest dyskutować o modułach AMD i CommonJS bez mówienia o słoniu w pokoju – ładowarkach skryptów. Obecnie, ładowanie skryptów jest środkiem do celu, tym celem jest modularny JavaScript, który może być używany w dzisiejszych aplikacjach – w tym celu, użycie kompatybilnego programu ładującego skrypty jest niestety konieczne. Aby uzyskać jak najwięcej z tego artykułu, zalecam zdobycie podstawowego zrozumienia, jak działają popularne narzędzia do ładowania skryptów, aby wyjaśnienia formatów modułów miały sens w kontekście.

Istnieje wiele świetnych programów ładujących do obsługi ładowania modułów w formatach AMD i CJS, ale moje osobiste preferencje to RequireJS i curl.js. Kompletne tutoriale dotyczące tych narzędzi są poza zakresem tego artykułu, ale mogę polecić przeczytanie postu Johna Hanna o curl.js i dokumentacji API RequireJS Jamesa Burke’a, aby dowiedzieć się więcej.

Z perspektywy produkcyjnej, użycie narzędzi optymalizacyjnych (takich jak optymalizator RequireJS) do konkatenacji skryptów jest zalecane do wdrożenia podczas pracy z takimi modułami. Co ciekawe, z Almond AMD shim, RequireJS nie musi być zwijane w rozmieszczonej witrynie, a to, co można uznać za program ładujący skrypty, może być łatwo przesunięte poza rozwój.

To powiedziawszy, James Burke prawdopodobnie powiedziałby, że zdolność do dynamicznego ładowania skryptów po załadowaniu strony nadal ma swoje przypadki użycia i RequireJS może pomóc również w tym. Mając te uwagi na uwadze, zacznijmy.

AMD A Format For Writing Modular JavaScript In The Browser

Ogólnym celem formatu AMD (Asynchronous Module Definition) jest dostarczenie rozwiązania dla modularnego JavaScriptu, z którego deweloperzy mogą korzystać już dziś. Zrodził się on z rzeczywistych doświadczeń Dojo z użyciem XHR+eval, a zwolennicy tego formatu chcieli uniknąć wszelkich przyszłych rozwiązań cierpiących z powodu słabości tych z przeszłości.

Format modułu AMD sam w sobie jest propozycją definiowania modułów, gdzie zarówno moduł jak i zależności mogą być ładowane asynchronicznie. Ma on wiele wyraźnych zalet, w tym bycie asynchronicznym i wysoce elastycznym z natury, co usuwa ścisłe sprzężenie, które można powszechnie znaleźć między kodem a tożsamością modułu. Wielu programistów lubi go używać i można by go uznać za niezawodny krok w kierunku systemu modułowego proponowanego dla ES Harmony.

AMD rozpoczął się jako projekt specyfikacji formatu modułów na liście CommonJS, ale ponieważ nie był w stanie osiągnąć pełnego konsensusu, dalszy rozwój formatu przeniósł się do grupy amdjs.

Dzisiaj jest on przyjęty przez projekty takie jak Dojo (1.7), MooTools (2.0), Firebug (1.8) a nawet jQuery (1.7). Chociaż termin CommonJS AMD format był widziany w naturze od czasu do czasu, najlepiej jest odnosić się do niego jako po prostu AMD lub Async Module support, ponieważ nie wszyscy uczestnicy na liście CJS chcieli go realizować.

Uwaga: Był czas, kiedy propozycja była określana jako Modules Transport/C, jednak ponieważ spec nie był nastawiony na transport istniejących modułów CJS, ale raczej na definiowanie modułów, bardziej sensowne było wybranie konwencji nazewnictwa AMD.

Początkowanie pracy z modułami

Dwie kluczowe koncepcje, których należy być świadomym, to idea metody define ułatwiającej definiowanie modułów i metody require obsługującej ładowanie zależności. define służy do definiowania nazwanych lub nienazwanych modułów na podstawie propozycji przy użyciu następującego podpisu:

Jak widać po komentarzach inline, module_id jest opcjonalnym argumentem, który jest zwykle wymagany tylko wtedy, gdy używane są narzędzia konkatenacji inne niż AMD (może być kilka innych przypadków brzegowych, w których jest to również przydatne). Kiedy ten argument jest pominięty, nazywamy moduł anonimowym.

Przy pracy z anonimowymi modułami, idea tożsamości modułu jest DRY, co sprawia, że uniknięcie duplikacji nazw plików i kodu jest banalne. Ponieważ kod jest bardziej przenośny, może być łatwo przeniesiony do innych lokalizacji (lub wokół systemu plików) bez potrzeby zmiany samego kodu lub zmiany jego identyfikatora. module_id jest równoważne ścieżkom do folderów w prostych pakietach i gdy nie jest używane w pakietach. Programiści mogą również uruchomić ten sam kod na wielu środowiskach po prostu używając optymalizatora AMD, który działa ze środowiskiem CommonJS, takim jak r.js.

Wracając do podpisu define, argument dependencies reprezentuje tablicę zależności, które są wymagane przez moduł, który definiujesz, a trzeci argument („funkcja definiująca”) jest funkcją, która jest wykonywana, aby zainicjować twój moduł. Moduł barebone może być zdefiniowany w następujący sposób:

Zrozumienie AMD: define()

require z drugiej strony jest typowo używane do ładowania kodu w pliku JavaScript najwyższego poziomu lub wewnątrz modułu, jeśli chcesz dynamicznie pobierać zależności. Przykładem jego użycia jest:

Zrozumienie AMD: require()

Dynamicznie ładowane zależności

Zrozumienie AMD: wtyczki

Następujący przykład definiowania wtyczki kompatybilnej z AMD:

Uwaga: Chociaż css! jest włączone do ładowania zależności CSS w powyższym przykładzie, ważne jest, aby pamiętać, że to podejście ma pewne zastrzeżenia, takie jak to, że nie jest w pełni możliwe ustalenie, kiedy CSS jest w pełni załadowany. W zależności od tego jak podchodzisz do budowania, może to również spowodować, że CSS zostanie dołączony jako zależność w zoptymalizowanym pliku, więc używaj CSS jako załadowanej zależności w takich przypadkach z ostrożnością.

Wczytywanie modułów AMD przy użyciu require.js

Loading AMD Modules Using curl.js

Modules With Deferred Dependencies

Why Is AMD A Better Choice For Writing Modular JavaScript?

  • Dostarcza jasną propozycję, jak podejść do definiowania elastycznych modułów.
  • Znacznie czystsze niż obecne globalne przestrzenie nazw i <script> rozwiązania znaczników, na których wielu z nas polega. Istnieje czysty sposób deklarowania samodzielnych modułów i zależności, które mogą mieć.
  • Definicje modułów są hermetyzowane, co pomaga nam uniknąć zanieczyszczenia globalnej przestrzeni nazw.
  • Działa lepiej niż niektóre alternatywne rozwiązania (np. CommonJS, któremu wkrótce się przyjrzymy). Nie ma problemów z cross-domenami, lokalnymi lub debugowaniem i nie ma zależności od narzędzi po stronie serwera, które mają być używane. Większość ładowarek AMD obsługuje ładowanie modułów w przeglądarce bez procesu budowania.
  • Zapewnia podejście „transportowe” do włączania wielu modułów do jednego pliku. Inne podejścia, takie jak CommonJS, muszą jeszcze uzgodnić format transportu.
  • Możliwe jest leniwe ładowanie skryptów, jeśli jest to potrzebne.

AMD Modules With Dojo

Definiowanie modułów kompatybilnych z AMD za pomocą Dojo jest dość proste. Tak jak powyżej, zdefiniuj wszystkie zależności modułu w tablicy jako pierwszy argument i podaj wywołanie zwrotne (fabrykę), która wykona moduł, gdy zależności zostaną załadowane. np.g:

define(, function( Tooltip ){ //Our dijit tooltip is now available for local use new Tooltip(...);});

Zauważ anonimową naturę modułu, który może być teraz konsumowany zarówno przez asynchroniczny program ładujący Dojo, RequireJS lub standardowy program ładujący dojo.require(), do którego możesz być przyzwyczajony.

Dla tych, którzy zastanawiają się nad odwoływaniem się do modułów, jest kilka interesujących gotchas, które warto znać. Chociaż AMD-advocated sposób odwoływania się do modułów deklaruje je na liście zależności z zestawem pasujących argumentów, nie jest to obsługiwane przez system kompilacji Dojo 1.6 – to naprawdę działa tylko dla ładowarek zgodnych z AMD. np.g:

define(, function( cookie, Tooltip ){ var cookieValue = cookie("cookieName"); new Tree(...); });

To ma wiele zalet w stosunku do zagnieżdżonego rozmieszczania nazw, ponieważ moduły nie muszą już bezpośrednio odwoływać się do pełnych przestrzeni nazw za każdym razem – wszystko, czego wymagamy, to ścieżka 'dojo/cookie’ w zależnościach, która po aliasie do argumentu, może być przywoływana przez tę zmienną. Eliminuje to konieczność wielokrotnego wpisywania 'dojo.’ w aplikacjach.

Uwaga: Chociaż Dojo 1.6 oficjalnie nie obsługuje modułów AMD opartych na użytkowniku (ani ładowania asynchronicznego), możliwe jest uzyskanie takiego działania w Dojo przy użyciu wielu różnych programów ładujących skrypty. Obecnie wszystkie moduły Dojo core i Dijit zostały przekształcone do składni AMD, a ulepszone ogólne wsparcie AMD prawdopodobnie pojawi się między 1.7 a 2.0.

Ostatnim problemem, o którym należy pamiętać jest to, że jeśli chcesz nadal używać systemu budowania Dojo lub chcesz zmigrować starsze moduły do nowszego stylu AMD, poniższa bardziej dosłowna wersja umożliwia łatwiejszą migrację. Zauważ, że dojo i dijit są również przywoływane jako zależności:

AMD Module Design Patterns (Dojo)

Jeśli śledziłeś któryś z moich poprzednich postów na temat korzyści płynących z wzorców projektowych, wiesz, że mogą one być bardzo efektywne w poprawieniu tego, jak podchodzimy do rozwiązywania typowych problemów programistycznych. John Hann wygłosił ostatnio świetną prezentację na temat wzorców projektowych modułów AMD obejmującą Singleton, Decorator, Mediator i inne. Gorąco polecam sprawdzenie jego slajdów, jeśli będziesz miał okazję.

Kilka próbek tych wzorców można znaleźć poniżej:

Wzór dekoratora:

Wzorzec adaptera

Moduły AMD z jQuery

Podstawy

W przeciwieństwie do Dojo, jQuery naprawdę przychodzi tylko z jednym plikiem, jednak biorąc pod uwagę opartą na wtyczkach naturę biblioteki, możemy zademonstrować, jak prosto jest zdefiniować moduł AMD, który z niej korzysta poniżej.

Jednak czegoś brakuje w tym przykładzie i jest to koncepcja rejestracji.

Registering jQuery As An Async-compatible Module

Jedną z kluczowych funkcji, które wylądowały w jQuery 1.7, było wsparcie dla rejestracji jQuery jako modułu asynchronicznego. Istnieje wiele kompatybilnych programów ładujących skrypty (w tym RequireJS i curl), które są zdolne do ładowania modułów przy użyciu formatu modułu asynchronicznego, a to oznacza, że mniej hacków jest wymaganych, aby wszystko działało.

W wyniku popularności jQuery, programy ładujące AMD muszą brać pod uwagę wiele wersji biblioteki ładowanych do tej samej strony, ponieważ idealnie nie chcesz, aby kilka różnych wersji ładowało się w tym samym czasie. Programiści mają możliwość albo szczególnego uwzględnienia tej kwestii, albo poinstruowania swoich użytkowników, że istnieją znane problemy ze skryptami stron trzecich i ich bibliotekami.

Co dodatek 1.7 wnosi do stołu, to fakt, że pomaga uniknąć problemów z innym kodem stron trzecich na stronie przypadkowo ładującym wersję jQuery na stronie, której właściciel się nie spodziewał. Nie chcesz innych instancji clobbering swoje własne i tak to może być z korzyścią.

Sposób, w jaki to działa jest to, że skrypt ładujący jest zatrudniony wskazuje, że obsługuje wiele wersji jQuery określając, że właściwość, define.amd.jQuery jest równa true. Dla tych, którzy są zainteresowani bardziej szczegółowymi szczegółami implementacji, rejestrujemy jQuery jako nazwany moduł, ponieważ istnieje ryzyko, że może on być konkatenowany z innymi plikami, które mogą używać metody AMD define(), ale nie używają odpowiedniego skryptu konkatenacji, który rozumie anonimowe definicje modułów AMD.

Nazwa AMD zapewnia koc bezpieczeństwa, który jest zarówno solidny, jak i bezpieczny dla większości przypadków użycia.

Mądrzejsze wtyczki jQuery

Ostatnio omówiłem tutaj kilka pomysłów i przykładów, jak wtyczki jQuery mogą być napisane przy użyciu wzorców Universal Module Definition (UMD). UMD definiują moduły, które mogą działać zarówno na kliencie, jak i na serwerze, a także ze wszystkimi popularnymi programami ładującymi skrypty dostępnymi w tej chwili. Podczas gdy jest to wciąż nowy obszar, w którym wiele koncepcji jest wciąż finalizowanych, zapraszam do obejrzenia próbek kodu w tytule sekcji AMD && CommonJS poniżej i daj mi znać, jeśli uważasz, że jest coś, co moglibyśmy zrobić lepiej.

Jakie frameworki ładujące skrypty & obsługują AMD?

In-browser:
Server-side:
  • RequireJS http://requirejs.org
  • PINF http://github.com/pinf/loader-js

AMD Conclusions

Powyżej przedstawiono bardzo trywialne przykłady tego, jak użyteczne mogą być moduły AMD, ale mam nadzieję, że stanowią one podstawę do zrozumienia, jak one działają.

Możesz być zainteresowany wiedzą, że wiele widocznych dużych aplikacji i firm używa obecnie modułów AMD jako części swojej architektury. Należą do nich IBM i BBC iPlayer, które podkreślają, jak poważnie ten format jest rozważany przez programistów na poziomie przedsiębiorstwa.

Więcej powodów, dla których wielu programistów decyduje się na użycie modułów AMD w swoich aplikacjach, może zainteresować Cię ten post Jamesa Burke’a.

CommonJS A Module Format Optimized For The Server

CommonJS są ochotniczą grupą roboczą, której celem jest projektowanie, prototypowanie i standaryzacja JavaScript API. Do tej pory próbowali ratyfikować standardy zarówno dla modułów jak i pakietów. Propozycja modułu CommonJS określa proste API do deklarowania modułów po stronie serwera i w przeciwieństwie do AMD próbuje objąć szerszy zestaw zagadnień, takich jak io, system plików, obietnice i inne.

Rozpoczęcie

Z punktu widzenia struktury, moduł CJS jest kawałkiem JavaScriptu wielokrotnego użytku, który eksportuje określone obiekty dostępne dla każdego zależnego kodu – zazwyczaj nie ma wrapperów funkcji wokół takich modułów (więc nie zobaczysz define użytego tutaj na przykład).

Na wysokim poziomie zawierają one w zasadzie dwie podstawowe części: wolną zmienną o nazwie exports, która zawiera obiekty, które moduł chce udostępnić innym modułom, oraz funkcję require, której moduły mogą używać do importowania eksportów innych modułów.

Zrozumienie CJS: require() i exports

Podstawowa konsumpcja exports

AMD-equivalent Of The First CJS Example

Konsumowanie wielu zależności

app.js
bar.js
exports.name = 'bar';
foo.js
require('./bar');exports.helloWorld = function(){ return 'Hello World!!''}

What Loaders & Frameworks Support CJS?

In-browser:
  • curl.js http://github.com/unscriptable/curl
  • SproutCore 1.1 http://sproutcore.com
  • PINF http://github.com/pinf/loader-js
  • (i więcej)
Server-side:

Is CJS Suitable For The Browser?

Są programiści, którzy uważają, że CommonJS lepiej nadaje się do rozwoju po stronie serwera, co jest jednym z powodów, dla których obecnie istnieje poziom niezgody co do tego, który format powinien i będzie używany jako standard de facto w erze przed-Harmony, idąc naprzód. Niektóre z argumentów przeciwko CJS zawierają uwagę, że wiele API CommonJS adresuje funkcje zorientowane na serwer, których po prostu nie dałoby się zaimplementować na poziomie przeglądarki w JavaScript – na przykład, io, system i js mogą być uznane za nie do zaimplementowania przez naturę ich funkcjonalności.

To powiedziawszy, warto wiedzieć, jak strukturyzować moduły CJS niezależnie od tego, abyśmy mogli lepiej ocenić, jak pasują do definiowania modułów, które mogą być używane wszędzie. Moduły, które mają zastosowanie zarówno na kliencie jak i na serwerze zawierają silniki walidacji, konwersji i szablonowania. Sposób, w jaki niektórzy programiści podchodzą do wyboru formatu do użycia, to wybór CJS, gdy moduł może być używany w środowisku po stronie serwera i użycie AMD, jeśli tak nie jest.

Jako że moduły AMD są zdolne do używania wtyczek i mogą definiować bardziej granularne rzeczy, takie jak konstruktory i funkcje, ma to sens. Moduły CJS są w stanie definiować tylko obiekty, które mogą być uciążliwe w pracy, jeśli próbujesz uzyskać z nich konstruktory.

Chociaż jest to poza zakresem tego artykułu, mogłeś również zauważyć, że były różne typy metod 'require’ wspomniane podczas omawiania AMD i CJS.

Zaniepokojenie podobną konwencją nazewniczą jest oczywiście zamieszanie i społeczność jest obecnie podzielona co do zalet globalnej funkcji require. Sugestia Johna Hanna jest taka, że zamiast nazywać ją 'require’, co prawdopodobnie nie osiągnęłoby celu informowania użytkowników o różnicy między globalnym a wewnętrznym require, bardziej sensowna może być zmiana nazwy metody globalnego loadera na coś innego (np. nazwę biblioteki). To właśnie z tego powodu program ładujący taki jak curl.js używa curl() w przeciwieństwie do require.

AMD && CommonJS Competing, But Equally Valid Standards

Podczas gdy ten artykuł kładzie większy nacisk na używanie AMD niż CJS, rzeczywistość jest taka, że oba formaty są ważne i mają zastosowanie.

AMD przyjmuje podejście browser-first do rozwoju, optując za asynchronicznym zachowaniem i uproszczoną kompatybilnością wsteczną, ale nie ma żadnej koncepcji File I/O. Obsługuje obiekty, funkcje, konstruktory, ciągi, JSON i wiele innych typów modułów, działających natywnie w przeglądarce. Jest niewiarygodnie elastyczny.

CommonJS z drugiej strony przyjmuje podejście server-first, zakładając synchroniczne zachowanie, brak globalnego bagażu jako John Hann odniósłby się do tego i próbuje zaspokoić przyszłość (na serwerze). Chodzi nam o to, że ponieważ CJS obsługuje moduły nieopakowane, może czuć się nieco bliżej specyfikacji ES.next/Harmony, uwalniając Cię od define() wrappera, który jest wymuszany przez AMD. Moduły CJS obsługują jednak tylko obiekty jako moduły.

Pomysł kolejnego formatu modułów może być zniechęcający, ale możesz być zainteresowany niektórymi próbkami pracy nad hybrydowymi modułami AMD/CJS i Univeral AMD/CJS.

Podstawowy hybrydowy format AMD (John Hann)

AMD/CommonJS Universal Module Definition (Wariacja 2, UMDjs)

Extensible UMD Plugins With (Wariacja autorstwa mojego i Thomasa Davisa).

core.js
myExtension.js
;(function ( name, definition ) { var theModule = definition(), hasDefine = typeof define === 'function', hasExports = typeof module !== 'undefined' && module.exports; if ( hasDefine ) { // AMD Module define(theModule); } else if ( hasExports ) { // Node.js Module module.exports = theModule; } else { // Assign to common namespaces or simply the global object (window) // account for for flat-file/global module extensions var obj = null; var namespaces = name.split("."); var scope = (this.jQuery || this.ender || this.$ || this); for (var i = 0; i 
app.js
$(function(){ // the plugin 'core' is exposed under a core namespace in // this example which we first cache var core = $.core; // use then use some of the built-in core functionality to // highlight all divs in the page yellow core.highlightAll(); // access the plugins (extensions) loaded into the 'plugin' // namespace of our core module: // Set the first div in the page to have a green background. core.plugin.setGreen("div:first"); // Here we're making use of the core's 'highlight' method // under the hood from a plugin loaded in after it // Set the last div to the 'errorColor' property defined in // our core module/plugin. If you review the code further down // you'll see how easy it is to consume properties and methods // between the core and other plugins core.plugin.setRed('div:last');});

ES Harmony Modules Of The Future

TC39, organ normalizacyjny odpowiedzialny za definiowanie składni i semantyki ECMAScript i jego przyszłych iteracji, składa się z wielu bardzo inteligentnych programistów. Niektórzy z tych deweloperów (tacy jak Alex Russell) uważnie obserwowali ewolucję wykorzystania JavaScriptu do rozwoju na dużą skalę w ciągu ostatnich kilku lat i są bardzo świadomi potrzeby lepszych cech języka do pisania bardziej modułowego JS.

Z tego powodu istnieją obecnie propozycje wielu ekscytujących dodatków do języka, w tym elastycznych modułów, które mogą działać zarówno na kliencie jak i na serwerze, moduł loader i więcej. W tej sekcji pokażę kilka próbek kodu składni modułów w ES.next, abyś mógł posmakować tego, co ma nadejść.

Uwaga: Chociaż Harmony jest wciąż w fazie propozycji, możesz już wypróbować (częściowe) cechy ES.next, które adresują natywne wsparcie dla pisania modularnego JavaScriptu dzięki kompilatorowi Google Traceur. Aby rozpocząć pracę z Traceur w mniej niż minutę, przeczytaj ten przewodnik. Istnieje również prezentacja JSConf na ten temat, którą warto obejrzeć, jeśli jesteś zainteresowany dowiedzeniem się więcej o projekcie.

Moduły z importem i eksportem

Jeśli przeczytałeś sekcje na temat modułów AMD i CJS, możesz być zaznajomiony z koncepcją zależności modułu (import) i eksportu modułu (lub, publiczne API / zmienne, które pozwalamy innym modułom konsumować). W ES.next, koncepcje te zostały zaproponowane w nieco bardziej zwięzły sposób, z zależnościami określanymi za pomocą słowa kluczowego import. export nie różni się znacznie od tego, czego moglibyśmy się spodziewać i myślę, że wielu programistów spojrzy na poniższy kod i natychmiast go "dostanie".

  • deklaracje importu wiążą eksport modułu jako zmienne lokalne i mogą być zmieniane nazwy, aby uniknąć kolizji/konfliktów nazw.
  • deklaracje eksportu deklarują, że lokalne wiązanie modułu jest zewnętrznie widoczne, tak że inne moduły mogą czytać eksport, ale nie mogą ich modyfikować. Co ciekawe, moduły mogą eksportować moduły potomne, ale nie mogą eksportować modułów, które zostały zdefiniowane gdzie indziej. Możesz również zmienić nazwy eksportów tak, aby ich zewnętrzne nazwy różniły się od ich nazw lokalnych.

Moduły ładowane ze zdalnych źródeł

Propozycje modułów obsługują również moduły, które są zdalnie oparte (np. wrapper API innej firmy), co upraszcza ładowanie modułów z zewnętrznych lokalizacji. Oto przykład naszego wciągnięcia modułu, który zdefiniowaliśmy powyżej i wykorzystania go:

Module Loader API

Proponowany moduł ładujący opisuje dynamiczny interfejs API do ładowania modułów w wysoce kontrolowanych kontekstach. Podpisy obsługiwane przez program ładujący obejmują load( url, moduleInstance, error) do ładowania modułów, createModule( object, globalModuleReferences) i inne. Oto kolejny przykład, w którym dynamicznie ładujemy moduł, który początkowo zdefiniowaliśmy. Zauważ, że w przeciwieństwie do ostatniego przykładu, gdzie wciągnęliśmy moduł ze zdalnego źródła, API modułu ładującego jest lepiej dostosowane do dynamicznych kontekstów.

Loader.load('http://addyosmani.com/factory/cakes.js', function(cakeFactory){ cakeFactory.oven.makeCupcake('chocolate'); });

CommonJS-like Modules For The Server

Dla programistów, którzy są zorientowani na serwer, system modułów proponowany dla ES.next nie jest ograniczony tylko do patrzenia na moduły w przeglądarce. Poniżej, dla przykładu, można zobaczyć moduł podobny do CJS proponowany do użycia na serwerze:

// io/File.jsexport function open(path) { ... };export function close(hnd) { ... };
module lexer from 'compiler/LexicalHandler';module stdlib from '@std'; //... scan(cmdline) ...

Classes With Constructors, Getters & Setters

Pojęcie klasy zawsze było kwestią sporną z purystami i do tej pory radziliśmy sobie albo spadając z powrotem na prototypową naturę JavaScriptu, albo poprzez użycie frameworków lub abstrakcji, które oferują możliwość użycia definicji klas w formie, która desugaruje do tego samego prototypowego zachowania.

W Harmony, klasy są częścią języka wraz z konstruktorami i (w końcu) pewnym poczuciem prawdziwej prywatności. W poniższych przykładach zawarłem kilka komentarzy inline, aby pomóc Ci zrozumieć, jak zbudowane są klasy, ale możesz również zauważyć brak słowa 'function’ w tym miejscu. To nie jest błąd literowy: TC39 podjęło świadomy wysiłek, aby zmniejszyć nasze nadużywanie słowa kluczowego function do wszystkiego i mamy nadzieję, że pomoże to uprościć sposób pisania kodu.

class Cake{ // We can define the body of a class' constructor // function by using the keyword 'constructor' followed // by an argument list of public and private declarations. constructor( name, toppings, price, cakeSize ){ public name = name; public cakeSize = cakeSize; public toppings = toppings; private price = price; } // As a part of ES.next's efforts to decrease the unnecessary // use of 'function' for everything, you'll notice that it's // dropped for cases such as the following. Here an identifier // followed by an argument list and a body defines a new method addTopping( topping ){ public(this).toppings.push(topping); } // Getters can be defined by declaring get before // an identifier/method name and a curly body. get allToppings(){ return public(this).toppings; } get qualifiesForDiscount(){ return private(this).price > 5; } // Similar to getters, setters can be defined by using // the 'set' keyword before an identifier set cakeSize( cSize ){ if( cSize 

ES Harmony Conclusions

As you can see, ES.next is coming with some exciting new additions. Although Traceur can be used to an extent to try our such features in the present, remember that it may not be the best idea to plan out your system to use Harmony (just yet). There are risks here such as specifications changing and a potential failure at the cross-browser level (IE9 for example will take a while to die) so your best bets until we have both spec finalization and coverage are AMD (for in-browser modules) and CJS (for those on the server).

Conclusions And Further Reading A Review

In this article we've reviewed several of the options available for writing modular JavaScript using modern module formats. These formats have a number of advantages over using the (classical) module pattern alone including: avoiding a need for developers to create global variables for each module they create, better support for static and dynamic dependency management, improved compatibility with script loaders, better (optional) compatibility for modules on the server and more.

In short, I recommend trying out what's been suggested today as these formats offer a lot of power and flexibility that can help when building applications based on many reusable blocks of functionality.

And that's it for now. If you have further questions about any of the topics covered today, feel free to hit me up on twitter and I'll do my best to help!

Copyright Addy Osmani, 2012. All Rights Reserved.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.