Moduláris JavaScript írása az AMD, CommonJS és ES Harmony segítségével

Tweet

Modularitás Az alkalmazás szétválasztásának fontossága

Mikor azt mondjuk, hogy egy alkalmazás moduláris, általában azt értjük alatta, hogy modulokban tárolt, egymástól erősen elválasztott, különálló funkciókból áll. Mint valószínűleg tudod, a laza csatolás megkönnyíti az alkalmazások könnyebb karbantarthatóságát azáltal, hogy ahol csak lehet, megszünteti a függőségeket. Ha ez hatékonyan van megvalósítva, akkor elég könnyű belátni, hogy a rendszer egyik részének változásai hogyan hatnak a másikra.

A hagyományosabb programozási nyelvekkel ellentétben azonban a JavaScript jelenlegi iterációja (ECMA-262) nem biztosítja a fejlesztők számára az ilyen kódmodulok tiszta, szervezett módon történő importálásának lehetőségét. Ez egyike azoknak a specifikációkkal kapcsolatos aggályoknak, amelyek nem igényeltek nagy gondolkodást egészen az utóbbi évekig, amikor is nyilvánvalóvá vált a szervezettebb JavaScript-alkalmazások szükségessége.

Ehelyett a fejlesztők jelenleg kénytelenek a modul vagy objektum szó szerinti minták variációihoz folyamodni. Ezek közül soknál a modulszkriptek a DOM-ban vannak felfűzve, a névtereket egyetlen globális objektum írja le, ahol még mindig lehetséges, hogy az architektúrában névütközések keletkeznek. A függőségkezelés kezelésére sincs tiszta megoldás kézi munka vagy harmadik féltől származó eszközök nélkül.

Míg ezekre a problémákra az ES Harmonyban érkeznek majd natív megoldások, a jó hír az, hogy a moduláris JavaScript írása még sosem volt ilyen egyszerű, és már ma elkezdheti.

Ebben a cikkben a moduláris JavaScript írásának három formátumát fogjuk megvizsgálni: AMD, CommonJS és a JavaScript következő verziójára, a Harmonyra vonatkozó javaslatok.

Előszó Egy megjegyzés a script betöltőkről

Nehéz úgy beszélni az AMD és a CommonJS modulokról, hogy ne beszélnénk az elefántról a szobában – a script betöltőkről. Jelenleg a szkript betöltés csak eszköz egy cél eléréséhez, ez a cél pedig a moduláris JavaScript, ami ma már használható az alkalmazásokban – ehhez sajnos szükség van egy kompatibilis szkript betöltő használatára. Annak érdekében, hogy a legtöbbet hozza ki ebből a cikkből, javaslom, hogy szerezzen alapvető ismereteket a népszerű szkriptbetöltő eszközök működéséről, hogy a modulformátumok magyarázatának legyen értelme a kontextusban.

Számos nagyszerű betöltő van a modulbetöltés kezelésére az AMD és CJS formátumokban, de személyes preferenciáim a RequireJS és a curl.js. Ezeknek az eszközöknek a teljes bemutatói kívül esnek e cikk keretein, de ajánlom John Hann curl.js-ről szóló bejegyzésének és James Burke RequireJS API dokumentációjának elolvasását.

A termelés szempontjából az ilyen modulokkal való munka során a telepítéshez ajánlott az optimalizáló eszközök (például a RequireJS optimalizáló) használata a szkriptek összefűzésére. Érdekes módon az Almond AMD shim segítségével a RequireJS-t nem kell a telepített webhelyen gördíteni, és amit szkript betöltőnek tekinthetünk, az könnyen áthelyezhető a fejlesztésen kívülre.

Ezzel együtt James Burke valószínűleg azt mondaná, hogy a szkriptek dinamikus betöltésének lehetősége az oldal betöltése után még mindig megvan a maga felhasználási területe, és a RequireJS ebben is segíthet. Ezeket a megjegyzéseket szem előtt tartva kezdjük el.

AMD A Format for Writing Modular JavaScript In The Browser

Az AMD (Asynchronous Module Definition) formátum általános célja, hogy olyan megoldást nyújtson a moduláris JavaScripthez, amelyet a fejlesztők már ma is használhatnak. A Dojo XHR+eval használatával szerzett valós tapasztalataiból született, és a formátum támogatói el akarták kerülni, hogy a jövőbeni megoldások szenvedjenek a korábbiak gyengeségeitől.

Az AMD modulformátum maga egy javaslat a modulok definiálására, ahol mind a modul, mind a függőségek aszinkron módon tölthetők be. Számos határozott előnye van, többek között az, hogy természeténél fogva aszinkron és rendkívül rugalmas, ami megszünteti azt a szoros csatolást, amit általában a kód és a modul identitása között találhatunk. Sok fejlesztő szívesen használja, és megbízható ugródeszkának tekinthető az ES Harmony számára javasolt modulrendszer felé vezető úton.

Az AMD egy modulformátum specifikációjának tervezeteként indult a CommonJS listán, de mivel nem sikerült teljes egyetértést elérni, a formátum továbbfejlesztése az amdjs csoporthoz került.

Már olyan projektek is elfogadják, mint a Dojo (1.7), a MooTools (2.0), a Firebug (1.8) vagy akár a jQuery (1.7). Bár a CommonJS AMD formátum kifejezést alkalmanként láttuk a vadonban, a legjobb, ha csak AMD vagy Async Module support néven hivatkozunk rá, mivel a CJS listán nem minden résztvevő kívánta ezt folytatni.

Megjegyzés: Volt idő, amikor a javaslatot Modules Transport/C néven emlegették, azonban mivel a specifikáció nem a meglévő CJS modulok szállítására, hanem inkább a modulok definiálására irányult, értelmesebb volt az AMD elnevezési konvenciót választani.

A modulokkal való kezdés

A két kulcsfogalom, amivel itt tisztában kell lenned, a define módszer gondolata a moduldefiníció megkönnyítésére és a require módszer a függőségi betöltés kezelésére. A define nevesített vagy névtelen modulok definiálására szolgál a javaslat alapján a következő aláírással:

Amint az inline kommentekből is látszik, a module_id egy opcionális argumentum, amelyre jellemzően csak akkor van szükség, ha nem-AMD konkatenációs eszközöket használunk (lehet néhány egyéb szélsőséges eset is, amikor hasznos lehet). Ha ez az argumentum kimarad, a modult anonimnak nevezzük.

A névtelen modulokkal való munka során a modul identitásának gondolata DRY, így triviális a fájlnevek és a kód duplikációjának elkerülése. Mivel a kód hordozhatóbb, könnyen áthelyezhető más helyekre (vagy a fájlrendszerben) anélkül, hogy magát a kódot módosítani kellene, vagy meg kellene változtatni az azonosítóját. A module_id az egyszerű csomagokban és a csomagokban nem használt mappák elérési útvonalával egyenértékű. A fejlesztők ugyanazt a kódot több környezetben is futtathatják, egyszerűen egy olyan AMD optimalizáló használatával, amely egy CommonJS-környezettel, például az r.js-szel működik.

Visszatérve a define aláíráshoz, a dependencies argumentum a definiálandó modul által megkövetelt függőségek tömbjét jelenti, a harmadik argumentum (‘definition function’) pedig egy olyan függvény, amely a modul instanciálásához végrehajtásra kerül. Egy barebone modult a következőképpen definiálhatunk:

Az AMD megértése: define()

A require viszont jellemzően a kód betöltésére szolgál egy felső szintű JavaScript-fájlban vagy egy modulon belül, ha dinamikusan kívánja lekérni a függőségeket. Egy példa a használatára:

Understanding AMD: require()

Dinamikusan betöltött függőségek

Understanding AMD: plugins

A következő példa egy AMD-kompatibilis plugin definiálására:

Megjegyzés: Bár a css! szerepel a CSS-függőségek betöltésére a fenti példában, fontos megjegyezni, hogy ennek a megközelítésnek van néhány hátulütője, például az, hogy nem lehet teljes mértékben megállapítani, hogy a CSS mikor töltődött be teljesen. Attól függően, hogy hogyan közelíti meg a buildet, ez azt is eredményezheti, hogy a CSS függőségként szerepel az optimalizált fájlban, ezért a CSS-t mint betöltött függőséget ilyen esetekben óvatosan használja.

Az AMD modulok betöltése require használatával.js

Loading AMD Modules Using curl.js

Modules With Deferred Dependencies

Why Is AMD A Better Choice For Writing Modular JavaScript?

  • Egyértelmű javaslatot ad arra, hogyan közelítsük meg a rugalmas modulok meghatározását.
  • Szignifikánsan tisztább, mint a jelenlegi globális névtér és <script> tag megoldások, amelyekre sokan támaszkodunk. Tiszta módja van az önálló modulok és esetleges függőségeik deklarálásának.
  • A moduldefiníciókat kapszulázzák, segít elkerülni a globális névtér szennyezését.
  • Jobb, mint néhány alternatív megoldás (pl. a CommonJS, amelyet rövidesen megvizsgálunk). Nem okoz gondot a tartományok közötti, a helyi vagy a hibakeresés, és nem függ a szerveroldali eszközök használatától. A legtöbb AMD betöltő támogatja a modulok betöltését a böngészőben, build folyamat nélkül.
  • Megoldja a “szállítási” megközelítést több modul egyetlen fájlba való beillesztéséhez. Más megközelítések, mint például a CommonJS, még nem állapodtak meg egy szállítási formátumban.
  • A szkriptek lusta betöltése is lehetséges, ha erre van szükség.

AMD modulok a Dojo-val

Az AMD-kompatibilis modulok definiálása a Dojo segítségével meglehetősen egyszerű. A fentiek szerint definiáljuk a modulfüggőségeket egy tömbben, mint első argumentumot, és adjunk meg egy callbacket (factory), amely a függőségek betöltése után végrehajtja a modult. e.g:

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

Megjegyezzük a modul anonim jellegét, amelyet most mind egy Dojo aszinkron betöltő, RequireJS vagy a szokásos dojo.require() modulletöltő, amelyet talán már megszoktunk használni, egyaránt fogyaszthat.

A modulhivatkozással kapcsolatban kíváncsiak számára van néhány érdekes kulissza, amit itt hasznos tudni. Bár a modulok hivatkozásának AMD által javasolt módja a modulokat a függőségi listában deklarálja egy sor megfelelő argumentummal, ezt a Dojo 1.6 build rendszere nem támogatja – ez tényleg csak AMD-kompatibilis betöltők esetén működik. e.g:

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

Ez sok előnnyel jár a beágyazott névtérrel szemben, mivel a moduloknak már nem kell minden alkalommal közvetlenül hivatkozniuk teljes névterekre – csak a ‘dojo/cookie’ elérési útvonalra van szükségünk a függőségekben, ami ha egyszer egy argumentumra aliasodik, akkor az adott változóval hivatkozhatunk rá. Így nem kell többször beírni a ‘dojo.’-t az alkalmazásokban.

Megjegyzés: Bár a Dojo 1.6 hivatalosan nem támogatja a felhasználó-alapú AMD modulokat (sem az aszinkron betöltést), ez számos különböző szkript betöltővel megvalósítható a Dojo-val. Jelenleg az összes Dojo core és Dijit modul át lett alakítva az AMD szintaxisra, és a javított általános AMD támogatás valószínűleg az 1.7 és 2.0 között fog landolni.

Az utolsó buktató, amivel tisztában kell lenned, hogy ha továbbra is a Dojo build rendszert szeretnéd használni, vagy a régebbi modulokat szeretnéd átállítani erre az újabb AMD-stílusra, a következő szókimondóbb verzió lehetővé teszi a könnyebb átállást. Figyeljük meg, hogy a dojo és a dijit is függőségként hivatkozik:

AMD Module Design Patterns (Dojo)

Ha követted bármelyik korábbi, a tervezési minták előnyeiről szóló bejegyzésemet, akkor tudod, hogy ezek rendkívül hatékonyan javíthatják azt, ahogyan a gyakori fejlesztési problémák megoldásának strukturálását megközelítjük. John Hann nemrégiben tartott egy kiváló előadást az AMD modul tervezési mintáiról, amely a Singleton, a Decorator, a Mediator és másokról szólt. Nagyon ajánlom, hogy nézd meg a diáit, ha van rá lehetőséged.

Ezeknek a mintáknak néhány mintája az alábbiakban található:

Decorator minta:

Adapter minta

AMD modulok a jQuery-vel

Az alapok

A Dojo-val ellentétben a jQuery valójában csak egy fájlt tartalmaz, azonban tekintettel a könyvtár plugin alapú jellegére, az alábbiakban bemutatjuk, hogy milyen egyszerű egy AMD modult definiálni, amely használja.

Valami azonban hiányzik ebből a példából, mégpedig a regisztráció fogalma.

A jQuery regisztrálása aszinkron kompatibilis modulként

A jQuery 1.7-ben landolt egyik legfontosabb funkció a jQuery aszinkron modulként való regisztrálásának támogatása. Számos kompatibilis szkript betöltő van (köztük a RequireJS és a curl), amelyek képesek a modulok aszinkron modul formátumú betöltésére, és ez azt jelenti, hogy kevesebb hackelésre van szükség a dolgok működéséhez.

A jQuery népszerűségéből adódóan az AMD betöltőknek figyelembe kell venniük a könyvtár több verziójának ugyanazon oldalra történő betöltését, mivel ideális esetben nem szeretnénk, ha egyszerre több különböző verzió töltődne be. A betöltőknek lehetőségük van arra, hogy vagy kifejezetten figyelembe veszik ezt a problémát, vagy pedig arra utasítják a felhasználóikat, hogy vannak ismert problémák a harmadik féltől származó szkriptekkel és azok könyvtáraival.

Az 1.7-es kiegészítés annyit tesz hozzá az asztalhoz, hogy segít elkerülni azokat a problémákat, amikor egy oldalon található más harmadik féltől származó kód véletlenül a jQuery egy olyan verzióját tölti be az oldalra, amelyre a tulajdonos nem számított. Nem akarjuk, hogy más példányok megzavarják a sajátunkat, ezért ez előnyös lehet.

A működés módja az, hogy az alkalmazott szkript betöltő jelzi, hogy több jQuery verziót támogat egy tulajdonság, a define.amd.jQuery true értékének megadásával. Akit érdekelnek a megvalósítás pontosabb részletei, a jQuery-t nevesített modulként regisztráljuk, mivel fennáll a veszélye, hogy más fájlokkal összefűzhető, amelyek esetleg az AMD define() módszerét használják, de nem használnak megfelelő összefűző szkriptet, amely megérti az anonim AMD moduldefiníciókat.

A névvel ellátott AMD egyfajta biztonsági takarót nyújt, amely egyszerre robusztus és biztonságos a legtöbb felhasználási esetben.

Tökéletesebb jQuery pluginok

A közelmúltban itt tárgyaltam néhány ötletet és példát arra, hogy a jQuery pluginokat hogyan lehetne Universal Module Definition (UMD) minták segítségével írni. Az UMD-k olyan modulokat definiálnak, amelyek mind a kliensen, mind a szerveren, valamint a jelenleg elérhető összes népszerű szkript betöltővel működhetnek. Bár ez még mindig egy új terület, ahol sok koncepció még véglegesítés alatt áll, nyugodtan nézze meg a kódmintákat az alábbi AMD && CommonJS fejezetcímben, és ossza meg velem, ha úgy érzi, hogy van valami, amit jobban tudnánk csinálni.

Milyen script betöltők & keretrendszerek támogatják az AMD-t?

Böngészőn belüli:
Szerveroldali:
  • RequireJS http://requirejs.org
  • PINF http://github.com/pinf/loader-js

AMD Következtetések

A fentiek nagyon triviális példák arra, hogy az AMD modulok valóban milyen hasznosak lehetnek, de remélhetőleg megalapozzák a működésük megértését.

Érdeklődésre tarthat számot, hogy jelenleg számos látható nagy alkalmazás és vállalat használ AMD modulokat az architektúrája részeként. Ezek közé tartozik az IBM és a BBC iPlayer, ami rávilágít arra, hogy a fejlesztők vállalati szinten mennyire komolyan veszik ezt a formátumot.

Azért, hogy miért választja sok fejlesztő az AMD modulok használatát az alkalmazásaiban, érdekelheti James Burke ezen bejegyzése.

CommonJS Egy szerverre optimalizált modulformátum

A CommonJS egy önkéntes munkacsoport, amelynek célja a JavaScript API-k tervezése, prototipizálása és szabványosítása. Eddig mind a modulok, mind a csomagok szabványainak ratifikálására tettek kísérletet. A CommonJS moduljavaslata egy egyszerű API-t határoz meg a modulok szerveroldali deklarálására, és az AMD-vel ellentétben megpróbálja lefedni a problémák szélesebb körét, például io, fájlrendszer, ígéretek és egyebek.

Kezdjük el

Szerkezeti szempontból a CJS modul egy újrafelhasználható JavaScript-darab, amely meghatározott objektumokat exportál, amelyeket bármely függő kód számára elérhetővé tesz – az ilyen modulok körül jellemzően nincsenek funkcióburkolatok (így például a define használatával itt nem találkozhatunk).

Magas szinten alapvetően két elsődleges részt tartalmaznak: egy exports nevű szabad változót, amely tartalmazza azokat az objektumokat, amelyeket a modul más modulok számára elérhetővé kíván tenni, és egy require függvényt, amelyet a modulok más modulok exportjainak importálására használhatnak.

A CJS megértése: require() és exportok

Az exportok alapvető fogyasztása

AMD-egyenértékű az első CJS-példa

Multi függőségek fogyasztása

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

Milyen betöltő & keretrendszerek támogatják a CJS-t?

Böngészőben:
  • curl.js http://github.com/unscriptable/curl
  • SproutCore 1.1 http://sproutcore.com
  • PINF http://github.com/pinf/loader-js
  • (és még több)
Szerveroldali:

A CJS alkalmas a böngészőhöz?

Vannak olyan fejlesztők, akik úgy érzik, hogy a CommonJS jobban alkalmas szerveroldali fejlesztésre, ami az egyik oka annak, hogy jelenleg bizonyos szintű nézeteltérés van arról, hogy melyik formátumot kell és fogjuk használni de facto szabványként a Harmony előtti korszakban a továbbiakban. A CJS elleni érvek között szerepel az a megjegyzés, hogy sok CommonJS API olyan szerver-orientált funkciókat kezel, amelyeket egyszerűen nem lehet böngésző-szinten JavaScriptben megvalósítani – például az io, system és js funkciók természetüknél fogva megvalósíthatatlannak tekinthetők.

Ezzel együtt hasznos tudni, hogy a CJS modulokat ettől függetlenül hogyan kell strukturálni, hogy jobban tudjuk értékelni, hogyan illeszkednek a mindenhol használható modulok definiálásakor. A kliensen és szerveren egyaránt alkalmazható modulok közé tartoznak az érvényesítési, konverziós és templating motorok. Néhány fejlesztő úgy közelíti meg a formátum kiválasztását, hogy a CJS-t választja, ha egy modul szerveroldali környezetben is használható, és az AMD-t használja, ha ez nem így van.

Mivel az AMD modulok képesek bővítményeket használni, és sokkal részletesebb dolgokat, például konstruktorokat és függvényeket definiálhatnak, ennek van értelme. A CJS modulok csak objektumokat képesek definiálni, amelyekkel fárasztó lehet dolgozni, ha konstruktorokat próbálsz belőlük kinyerni.

Bár nem tartozik ennek a cikknek a tárgykörébe, azt is észrevehetted, hogy az AMD és a CJS tárgyalásakor különböző típusú ‘require’ módszereket említettek.

A hasonló elnevezési konvencióval kapcsolatos aggodalom természetesen a zűrzavar, és a közösség jelenleg megosztott a globális require függvény érdemeit illetően. John Hann javaslata itt az, hogy ahelyett, hogy ‘require’-nek neveznénk, ami valószínűleg nem érné el azt a célt, hogy tájékoztassuk a felhasználókat a globális és a belső require közötti különbségről, talán több értelme lenne átnevezni a globális betöltő metódust valami másra (pl. a könyvtár nevére). Ez az oka annak, hogy egy olyan betöltő, mint a curl.js a curl() nevet használja a require helyett.

AMD && CommonJS Versengő, de egyformán érvényes szabványok

Míg ez a cikk nagyobb hangsúlyt fektetett az AMD használatára a CJS helyett, a valóság az, hogy mindkét formátum érvényes, és van haszna.

Az AMD a böngésző-első megközelítést alkalmazza a fejlesztés során, az aszinkron viselkedést és az egyszerűsített visszafelé kompatibilitást választja, de nem rendelkezik a File I/O fogalmával. Támogatja az objektumokat, függvényeket, konstruktorokat, stringeket, JSON-t és sok más típusú modult, amelyek natívan a böngészőben futnak. Hihetetlenül rugalmas.

A CommonJS ezzel szemben szerver-első megközelítést alkalmaz, szinkron viselkedést feltételez, nincs globális poggyász, ahogy John Hann nevezné, és megpróbál gondoskodni a jövőről (a szerveren). Ez alatt azt értjük, hogy mivel a CJS támogatja a csomagolatlan modulokat, kicsit közelebb érezheti magát az ES.next/Harmony specifikációkhoz, megszabadulva az AMD által erőltetett define() wrappertől. A CJS modulok azonban csak az objektumokat támogatják modulként.

Noha egy újabb modulformátum gondolata ijesztő lehet, érdekelhet néhány minta a hibrid AMD/CJS és Univeral AMD/CJS modulokkal kapcsolatos munkákból.

Basic AMD Hybrid Format (John Hann)

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

Extensible UMD Plugins With (Variation by myself and Thomas Davis).

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

Az ECMAScript és jövőbeli iterációi szintaxisának és szemantikájának meghatározásával megbízott szabványügyi testület, a TC39 számos nagyon intelligens fejlesztőből áll. E fejlesztők közül néhányan (például Alex Russell) az elmúlt néhány évben szorosan figyelemmel kísérték a JavaScript nagyléptékű fejlesztésekhez való használatának alakulását, és nagyon is tisztában vannak azzal, hogy a modulárisabb JS írásához jobb nyelvi tulajdonságokra van szükség.

Ezért jelenleg számos izgalmas kiegészítésre vannak javaslatok a nyelvhez, többek között olyan rugalmas modulokra, amelyek kliensen és szerveren is működhetnek, modulbetöltőre és egyebekre. Ebben a részben mutatok néhány kódmintát az ES.next modulok szintaxisából, így ízelítőt kaphatsz abból, hogy mi várható.

Megjegyzés: Bár a Harmony még mindig a javaslatok fázisában van, a Google Traceur fordítójának köszönhetően már most is kipróbálhatod az ES.next olyan (részleges) funkcióit, amelyek a moduláris JavaScript írásának natív támogatásával foglalkoznak. Ha kevesebb mint egy perc alatt be akarja vetni a Traceurt, olvassa el ezt a kezdeti útmutatót. Van egy JSConf prezentáció is róla, amit érdemes megnézni, ha többet szeretne megtudni a projektről.

Modulok importtal és exporttal

Ha már végigolvasta az AMD és CJS modulokról szóló részeket, akkor talán már ismeri a modulfüggőségek (importok) és a modul exportok (vagy a nyilvános API/változók, amelyeket más moduloknak engedélyezünk fogyasztani) fogalmát. Az ES.next-ben ezeket a fogalmakat kissé tömörebben javasoltuk, a függőségeket egy import kulcsszóval adjuk meg. export nem sokban különbözik attól, amire számíthatunk, és szerintem sok fejlesztő ránéz az alábbi kódra, és azonnal "megérti".

  • Az import deklarációk egy modul exportjait helyi változóként kötik meg, és átnevezhetők a névütközések/konfliktusok elkerülése érdekében.
  • Az export deklarációk deklarálják, hogy egy modul helyi kötése kívülről látható, így más modulok olvashatják az exportokat, de nem módosíthatják azokat. Érdekes módon a modulok exportálhatnak gyermekmodulokat, azonban nem exportálhatnak máshol definiált modulokat. Az exportok átnevezhetők úgy is, hogy a külső nevük eltérjen a helyi nevüktől.

Távoli forrásból betöltött modulok

A moduljavaslatok a távoli forrásból (pl. egy harmadik féltől származó API wrapper) származó modulokra is vonatkoznak, így leegyszerűsítve a modulok külső helyekről történő betöltését. Íme egy példa arra, hogy a fent definiált modult behúzzuk és felhasználjuk:

Modulbetöltő API

A modulbetöltő javaslat egy dinamikus API-t ír le a modulok erősen ellenőrzött kontextusban történő betöltésére. A betöltő által támogatott aláírások közé tartozik a load( url, moduleInstance, error) a modulok betöltéséhez, createModule( object, globalModuleReferences) és mások. Itt egy másik példa arra, hogy dinamikusan betöltjük az eredetileg definiált modult. Vegyük észre, hogy az előző példával ellentétben, ahol egy távoli forrásból húztunk be egy modult, a modultöltő API jobban alkalmas dinamikus kontextusokra.

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

CommonJS-szerű modulok a szerverhez

A szerverorientált fejlesztők számára az ES.next számára javasolt modulrendszer nem csak arra korlátozódik, hogy a modulokat a böngészőben nézzük. Az alábbiakban példaként a szerveren való használatra javasolt CJS-szerű modulokat láthatjuk:

// 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

Az osztály fogalma mindig is vitatott kérdés volt a puristák körében, és eddig vagy a JavaScript prototípusos természetéhez való visszatéréssel, vagy olyan keretrendszerek vagy absztrakciók használatával boldogultunk, amelyek lehetőséget biztosítanak az osztálydefiníciók olyan formában történő használatára, amely ugyanazt a prototípusos viselkedést teszi lehetővé.

A Harmonyban az osztályok a nyelv részeként jönnek, a konstruktorokkal és (végre) a valódi adatvédelem bizonyos fokával együtt. A következő példákban néhány soron belüli megjegyzést tettem, hogy segítsek megérteni az osztályok felépítését, de az is feltűnhet, hogy a ‘function’ szó hiányzik innen. Ez nem elírási hiba: A TC39 tudatosan igyekszik csökkenteni a mindenre vonatkozó function kulcsszóval való visszaélést, és reméljük, hogy ez segít leegyszerűsíteni a kódírásunkat.

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.

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.