Modulair JavaScript schrijven met AMD, CommonJS & ES Harmony

Tweet

Modulariteit Het belang van ontkoppeling van uw toepassing

Wanneer we zeggen dat een toepassing modulair is, bedoelen we over het algemeen dat deze is opgebouwd uit een reeks in hoge mate ontkoppelde, afzonderlijke stukken functionaliteit die zijn opgeslagen in modules. Zoals je waarschijnlijk weet, losse koppeling vergemakkelijkt onderhoudbaarheid van apps door het verwijderen van afhankelijkheden waar mogelijk. Wanneer dit efficiënt wordt geïmplementeerd, is het vrij eenvoudig om te zien hoe veranderingen in een deel van een systeem van invloed kunnen zijn op een ander deel.

In tegenstelling tot sommige meer traditionele programmeertalen biedt de huidige iteratie van JavaScript (ECMA-262) ontwikkelaars echter niet de middelen om dergelijke modules van code op een schone, georganiseerde manier te importeren. Het is een van de problemen met specificaties waarover niet lang is nagedacht, totdat in de afgelopen jaren de behoefte aan beter georganiseerde JavaScript-toepassingen duidelijk werd.

In plaats daarvan zijn ontwikkelaars op dit moment aangewezen op variaties van de module of object literal patronen. Met veel van deze, module scripts worden aan elkaar geregen in het DOM met namespaces worden beschreven door een enkele globale object waar het nog steeds mogelijk is om naamgeving botsingen in uw architectuur op te lopen. Er is ook geen nette manier om afhankelijkheden te beheren zonder handmatige inspanning of tools van derden.

Terwijl native oplossingen voor deze problemen in ES Harmony zullen verschijnen, is het goede nieuws dat het schrijven van modulair JavaScript nog nooit zo eenvoudig is geweest en dat je er vandaag al mee kunt beginnen.

In dit artikel gaan we kijken naar drie formats voor het schrijven van modulair JavaScript: AMD, CommonJS en voorstellen voor de volgende versie van JavaScript, Harmony.

Prelude A Note On Script Loaders

Het is moeilijk om AMD en CommonJS modules te bespreken zonder het te hebben over de olifant in de kamer – script loaders. Op dit moment is het laden van scripts een middel om een doel te bereiken, dat doel is modulaire JavaScript die vandaag de dag in toepassingen kan worden gebruikt – daarvoor is het gebruik van een compatibele scriptloader helaas noodzakelijk. Om het meeste uit dit artikel te halen, raad ik aan een basiskennis te hebben van hoe populaire script-laadtools werken, zodat de uitleg van module-indelingen zinvol is in de context.

Er zijn een aantal geweldige laders voor het afhandelen van het laden van modules in de AMD- en CJS-indelingen, maar mijn persoonlijke voorkeuren zijn RequireJS en curl.js. Volledige tutorials over deze tools vallen buiten het bereik van dit artikel, maar ik kan je aanraden John Hann’s post over curl.js en James Burke’s RequireJS API documentatie te lezen voor meer.

Vanuit een productie perspectief, wordt het gebruik van optimalisatie tools (zoals de RequireJS optimizer) om scripts aan elkaar te plakken aanbevolen voor deployment wanneer je met zulke modules werkt. Interessant is dat met de Almond AMD shim, RequireJS niet hoeft te worden gerold in de ingezette site en wat je zou kunnen beschouwen als een script loader kan gemakkelijk worden verschoven buiten ontwikkeling.

Dat gezegd hebbende, James Burke zou waarschijnlijk zeggen dat de mogelijkheid om dynamisch scripts te laden na het laden van de pagina nog steeds zijn use cases heeft en RequireJS kan hier ook bij helpen. Met deze aantekeningen in het achterhoofd kunnen we aan de slag.

AMD A Format For Writing Modular JavaScript In The Browser

Het algemene doel van het AMD-formaat (Asynchronous Module Definition) is om een oplossing te bieden voor modulaire JavaScript die ontwikkelaars vandaag de dag kunnen gebruiken. Het is voortgekomen uit Dojo’s praktijkervaring met XHR+eval en voorstanders van dit formaat wilden voorkomen dat toekomstige oplossingen zouden lijden onder de zwakke punten van die in het verleden.

Het AMD-moduleformaat zelf is een voorstel voor het definiëren van modules waarbij zowel de module als de afhankelijkheden asynchroon kunnen worden geladen. Het heeft een aantal duidelijke voordelen, waaronder het feit dat het zowel asynchroon als zeer flexibel van aard is, waardoor de strakke koppeling die men gewoonlijk aantreft tussen code en module-identiteit wordt opgeheven. Veel ontwikkelaars maken er graag gebruik van en men zou het kunnen beschouwen als een betrouwbare opstap naar het modulesysteem dat wordt voorgesteld voor ES Harmony.

AMD begon als een concept specificatie voor een module formaat op de CommonJS lijst, maar omdat het niet in staat was om volledige consensus te bereiken, verhuisde de verdere ontwikkeling van het formaat naar de amdjs groep.

Heden ten dage wordt het omarmd door projecten waaronder Dojo (1.7), MooTools (2.0), Firebug (1.8) en zelfs jQuery (1.7). Hoewel de term CommonJS AMD-formaat af en toe in het wild is gezien, is het het beste om ernaar te verwijzen als gewoon AMD of Async Module support, omdat niet alle deelnemers op de CJS-lijst dit wilden nastreven.

Opmerking: Er was een tijd dat het voorstel Modules Transport/C werd genoemd, maar aangezien de spec niet was gericht op het transporteren van bestaande CJS-modules, maar eerder op het definiëren van modules, was het logischer om te kiezen voor de AMD-naamgevingsconventie.

Getting Started With Modules

De twee belangrijkste concepten waarvan u zich hier bewust moet zijn, zijn het idee van een define methode voor het vergemakkelijken van moduledefinitie en een require methode voor het afhandelen van het laden van afhankelijkheden. define wordt gebruikt om modules met of zonder naam te definiëren, gebaseerd op het voorstel met de volgende signatuur:

Zoals u kunt zien aan het inline commentaar, is de module_id een optioneel argument dat gewoonlijk alleen nodig is wanneer niet-AMD concatenatiegereedschappen worden gebruikt (er kunnen enkele andere randgevallen zijn waarin het ook nuttig is). Wanneer dit argument wordt weggelaten, noemen we de module anoniem.

Wanneer we met anonieme modules werken, is het idee van de identiteit van een module DRY, waardoor het triviaal is om duplicatie van bestandsnamen en code te vermijden. Omdat de code beter overdraagbaar is, kan deze gemakkelijk worden verplaatst naar andere locaties (of in het bestandssysteem) zonder dat de code zelf hoeft te worden gewijzigd of de ID hoeft te worden aangepast. De module_id is equivalent aan mappaden in eenvoudige packages en wanneer ze niet gebruikt worden in packages. Ontwikkelaars kunnen dezelfde code ook op meerdere omgevingen draaien door gewoon een AMD optimizer te gebruiken die werkt met een CommonJS omgeving zoals r.js.

Terug naar de define signature, het dependencies argument vertegenwoordigt een array van dependencies die nodig zijn voor de module die je definieert en het derde argument (‘definition function’) is een functie die wordt uitgevoerd om je module te instantiëren. Een barebone module zou als volgt gedefinieerd kunnen worden:

Onder AMD: define()

require daarentegen wordt meestal gebruikt om code te laden in een JavaScript-bestand op het hoogste niveau of binnen een module als u dynamisch afhankelijkheden wilt ophalen. Een voorbeeld van het gebruik ervan is:

Uitleg AMD: require()

Dynamisch geladen afhankelijkheden

Uitleg AMD: plugins

Het volgende is een voorbeeld van het definiëren van een AMD-compatibele plugin:

Opmerking: Hoewel css! is opgenomen voor het laden van CSS afhankelijkheden in het bovenstaande voorbeeld, is het belangrijk om te onthouden dat deze aanpak enkele voorbehouden heeft, zoals het niet volledig mogelijk zijn om vast te stellen wanneer de CSS volledig geladen is. Afhankelijk van hoe u uw bouw benadert, kan het er ook toe leiden dat CSS wordt opgenomen als een afhankelijkheid in het geoptimaliseerde bestand, dus gebruik CSS als een geladen afhankelijkheid in zulke gevallen met voorzichtigheid.

Laden van AMD modules met require.js

Loading AMD Modules Using curl.js

Modules With Deferred Dependencies

Why Is AMD A Better Choice For Writing Modular JavaScript?

  • Geeft een duidelijk voorstel voor hoe het definiëren van flexibele modules te benaderen.
  • Significant schoner dan de huidige global namespace en <script> tag oplossingen waar velen van ons op vertrouwen. Er is een schone manier om stand-alone modules en de afhankelijkheden die ze kunnen hebben aan te geven.
  • Module definities zijn ingekapseld, waardoor we vervuiling van de global namespace kunnen voorkomen.
  • Werkt beter dan sommige alternatieve oplossingen (bijv. CommonJS, waar we binnenkort naar zullen kijken). Heeft geen problemen met cross-domain, local of debugging en is niet afhankelijk van server-side tools die gebruikt moeten worden. De meeste AMD loaders ondersteunen het laden van modules in de browser zonder een build proces.
  • Biedt een ’transport’ benadering voor het opnemen van meerdere modules in een enkel bestand. Andere benaderingen zoals CommonJS moeten het nog eens worden over een transport formaat.
  • Het is mogelijk om scripts lui te laden als dit nodig is.

AMD-modules met Dojo

Het definiëren van AMD-compatibele modules met behulp van Dojo is vrij eenvoudig. Definieer zoals hierboven alle moduleafhankelijkheden in een array als eerste argument en geef een callback (fabriek) die de module zal uitvoeren zodra de afhankelijkheden zijn geladen. e.g:

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

Noteer het anonieme karakter van de module die nu kan worden geconsumeerd door zowel een Dojo asynchrone loader, RequireJS of de standaard dojo.require() module loader die je misschien gewend bent te gebruiken.

Voor degenen die zich afvragen hoe het zit met module-referencing, zijn er een paar interessante gotchas die nuttig zijn om hier te weten. Hoewel de door AMD aanbevolen manier om naar modules te verwijzen ze in de afhankelijkheidslijst declareert met een set bijpassende argumenten, wordt dit niet ondersteund door het Dojo 1.6 bouwsysteem – het werkt echt alleen voor AMD-compliant loaders. e.g:

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

Dit heeft veel voordelen ten opzichte van geneste namespacing, omdat modules niet meer elke keer direct naar complete namespaces hoeven te verwijzen – alles wat we nodig hebben is het ‘dojo/cookie’ pad in dependencies, dat eenmaal gealiased naar een argument, kan worden gerefereerd door die variabele. Hierdoor is het niet meer nodig om herhaaldelijk ‘dojo.’ in te typen in je applicaties.

Opmerking: Hoewel Dojo 1.6 officieel geen gebruikersgebaseerde AMD-modules ondersteunt (noch asynchroon laden), is het mogelijk om dit werkend te krijgen met Dojo door gebruik te maken van een aantal verschillende scriptloaders. Op dit moment zijn alle Dojo core en Dijit modules getransformeerd naar de AMD syntax en verbeterde algehele AMD ondersteuning zal waarschijnlijk landen tussen 1.7 en 2.0.

De laatste gotcha om op te letten is dat als je het Dojo build systeem wilt blijven gebruiken of oudere modules wilt migreren naar deze nieuwere AMD-stijl, de volgende meer verbose versie de migratie makkelijker maakt. Merk op dat dojo en dijit ook als afhankelijkheden worden genoemd:

AMD Module Design Patterns (Dojo)

Als je een van mijn eerdere berichten over de voordelen van ontwerppatronen hebt gevolgd, weet je dat ze zeer effectief kunnen zijn bij het verbeteren van de manier waarop we oplossingen voor veelvoorkomende ontwikkelingsproblemen structureren. John Hann gaf onlangs een uitstekende presentatie over AMD module design patterns met Singleton, Decorator, Mediator en anderen. Ik raad je ten zeerste aan zijn slides te bekijken als je de kans krijgt.

Enkele voorbeelden van deze patronen zijn hieronder te vinden:

Decorator patroon:

Adapterpatroon

AMD-modules met jQuery

De basis

In tegenstelling tot Dojo wordt jQuery eigenlijk maar met één bestand geleverd, maar gezien de plugin-gebaseerde aard van de bibliotheek, kunnen we hieronder demonstreren hoe eenvoudig het is om een AMD-module te definiëren die er gebruik van maakt.

Er ontbreekt echter iets in dit voorbeeld en dat is het concept van registratie.

Registratie van jQuery als een asynchrone module

Een van de belangrijkste functies die in jQuery 1.7 landde, was ondersteuning voor het registreren van jQuery als een asynchrone module. Er zijn een aantal compatibele script loaders (waaronder RequireJS en curl) die in staat zijn om modules te laden met behulp van een asynchrone module-formaat en dit betekent dat er minder hacks nodig zijn om dingen werkend te krijgen.

Als gevolg van de populariteit van jQuery, AMD loaders moeten rekening houden met meerdere versies van de bibliotheek wordt geladen in dezelfde pagina als je idealiter niet wilt dat verschillende versies te laden op hetzelfde moment. Laders hebben de optie om ofwel specifiek rekening te houden met deze kwestie of hun gebruikers te instrueren dat er bekende problemen zijn met scripts van derden en hun bibliotheken.

Wat de 1.7 toevoeging aan de tafel brengt is dat het helpt problemen te voorkomen met andere code van derden op een pagina die per ongeluk een versie van jQuery op de pagina laadt die de eigenaar niet verwachtte. Je wilt niet dat andere instanties je eigen code omzeilen en dus kan dit van voordeel zijn.

De manier waarop dit werkt is dat de gebruikte script loader aangeeft dat het meerdere jQuery versies ondersteunt door aan te geven dat een eigenschap, define.amd.jQuery gelijk is aan true. Voor degenen die geïnteresseerd zijn in meer specifieke implementatiedetails, registreren we jQuery als een benoemde module, omdat het risico bestaat dat het kan worden aaneengeschakeld met andere bestanden die AMD’s define()-methode kunnen gebruiken, maar geen goed aaneenschakelscript gebruiken dat anonieme AMD-moduledefinities begrijpt.

De benoemde AMD biedt een veiligheidsdeken van zowel robuust als veilig zijn voor de meeste use-cases.

Slimmere jQuery Plugins

Ik heb hier onlangs enkele ideeën en voorbeelden besproken van hoe jQuery plugins zouden kunnen worden geschreven met behulp van Universal Module Definition (UMD) patronen. UMD’s definiëren modules die kunnen werken op zowel de client en server, maar ook met alle populaire script loaders beschikbaar op dit moment. Hoewel dit nog een nieuw gebied is met veel concepten die nog moeten worden uitgewerkt, voel je vrij om te kijken naar de code voorbeelden in de sectie titel AMD && CommonJS hieronder en laat het me weten als je denkt dat er iets is wat we beter zouden kunnen doen.

Welke Script Loaders & Frameworks ondersteunen AMD?

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

AMD Conclusies

Het bovenstaande zijn zeer triviale voorbeelden van hoe nuttig AMD-modules werkelijk kunnen zijn, maar ze bieden hopelijk een basis om te begrijpen hoe ze werken.

Het zal u misschien interesseren om te weten dat veel zichtbare grote toepassingen en bedrijven momenteel AMD modules gebruiken als onderdeel van hun architectuur. Deze omvatten IBM en de BBC iPlayer, die laten zien hoe serieus dit formaat wordt overwogen door ontwikkelaars op bedrijfsniveau.

Voor meer redenen waarom veel ontwikkelaars ervoor kiezen om AMD-modules te gebruiken in hun toepassingen, bent u wellicht geïnteresseerd in dit bericht van James Burke.

CommonJS A Module Format Optimized For The Server

CommonJS is een vrijwilligerswerkgroep die zich ten doel stelt JavaScript API’s te ontwerpen, te prototypen en te standaardiseren. Tot nu toe hebben ze geprobeerd standaarden te bekrachtigen voor zowel modules als pakketten. Het CommonJS module voorstel specificeert een eenvoudige API voor het declareren van modules server-side en in tegenstelling tot AMD probeert het een bredere set van onderwerpen te dekken zoals io, filesystem, promises en meer.

Aan de slag

Op structureel niveau is een CJS-module een herbruikbaar stuk JavaScript dat specifieke objecten exporteert die beschikbaar worden gesteld aan afhankelijke code – er zijn meestal geen functie-wrappers rond dergelijke modules (dus u zult define hier bijvoorbeeld niet gebruikt zien worden).

Op een hoog niveau bevatten ze in principe twee primaire onderdelen: een vrije variabele met de naam exports die de objecten bevat die een module beschikbaar wil maken voor andere modules en een require functie die modules kunnen gebruiken om de exports van andere modules te importeren.

Inzicht in CJS: require() en exports

Basisconsumptie van exports

AMD-equivalent van het eerste CJS-voorbeeld

Consumptie van meerdere afhankelijkheden

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

Welke Loaders & Frameworks ondersteunen CJS?

In-browser:
  • curl.js http://github.com/unscriptable/curl
  • SproutCore 1.1 http://sproutcore.com
  • PINF http://github.com/pinf/loader-js
  • (en meer)
Server-side:

Is CJS geschikt voor de Browser?

Er zijn ontwikkelaars die vinden dat CommonJS beter geschikt is voor server-side ontwikkeling. Dit is een van de redenen dat er momenteel onenigheid is over welk formaat gebruikt moet en zal worden als de de facto standaard in het pre-Harmony tijdperk. Enkele van de argumenten tegen CJS zijn de opmerking dat veel CommonJS API’s server-georiënteerde functies adresseren die men eenvoudigweg niet op browserniveau in JavaScript zou kunnen implementeren – zo zouden io, system en js als niet-implementeerbaar kunnen worden beschouwd door de aard van hun functionaliteit.

Dat gezegd hebbende, is het nuttig om te weten hoe CJS-modules hoe dan ook moeten worden gestructureerd, zodat we beter kunnen waarderen hoe ze passen bij het definiëren van modules die overal kunnen worden gebruikt. Modules die zowel op de client als op de server worden gebruikt, zijn onder meer validatie-, conversie- en templating-engines. De manier waarop sommige ontwikkelaars de keuze van het te gebruiken formaat benaderen is door te kiezen voor CJS wanneer een module in een server-side omgeving kan worden gebruikt en door AMD te gebruiken als dat niet het geval is.

Aangezien AMD-modules gebruik kunnen maken van plugins en meer granulaire zaken kunnen definiëren zoals constructors en functies is dit logisch. CJS modules zijn alleen in staat om objecten te definiëren, die vervelend kunnen zijn om mee te werken als je probeert om constructors uit hen te verkrijgen.

Hoewel het buiten het bestek van dit artikel valt, is het u misschien ook opgevallen dat er verschillende soorten ‘require’ methoden werden genoemd bij de bespreking van AMD en CJS.

Het probleem met een gelijksoortige naamgeving is natuurlijk verwarring en de gemeenschap is momenteel verdeeld over de verdiensten van een globale require functie. John Hann’s suggestie hier is dat in plaats van het “require” te noemen, wat waarschijnlijk niet het doel zou bereiken om gebruikers te informeren over het verschil tussen een globale en innerlijke require, het wellicht zinvoller is om de globale loader methode een andere naam te geven (b.v. de naam van de bibliotheek). Het is om deze reden dat een loader zoals curl.js curl() gebruikt in plaats van require.

AMD && CommonJS Concurrerende, maar even geldige standaarden

Hoewel in dit artikel meer nadruk is gelegd op het gebruik van AMD dan op CJS, is de realiteit dat beide formaten geldig zijn en een nut hebben.

AMD volgt een browser-eerst benadering van ontwikkeling, kiest voor asynchroon gedrag en vereenvoudigde achterwaartse compatibiliteit, maar het heeft geen concept van File I/O. Het ondersteunt objecten, functies, constructors, strings, JSON en vele andere soorten modules, die in de browser draaien. Het is ongelooflijk flexibel.

CommonJS aan de andere kant gaat uit van een server-first benadering, uitgaande van synchroon gedrag, geen globale bagage zoals John Hann het zou noemen en het probeert te voorzien in de toekomst (op de server). Wat we hiermee bedoelen is dat CJS, omdat het unwrapped modules ondersteunt, iets meer kan aanleunen bij de ES.next/Harmony specificaties, waardoor u bevrijd bent van de define() wrapper die AMD afdwingt. CJS modules ondersteunen echter alleen objecten als modules.

Hoewel het idee van weer een ander module formaat ontmoedigend kan zijn, bent u wellicht geïnteresseerd in enkele voorbeelden van werk aan hybride AMD/CJS en Univeral AMD/CJS modules.

Basic AMD Hybrid Format (John Hann)

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

Extensible UMD Plugins With (Variatie van mijzelf en 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

TC39, het normalisatie-orgaan dat is belast met het definiëren van de syntaxis en semantiek van ECMAScript en de toekomstige iteraties daarvan, bestaat uit een aantal zeer intelligente ontwikkelaars. Sommige van deze ontwikkelaars (zoals Alex Russell) hebben de evolutie van het JavaScript-gebruik voor grootschalige ontwikkeling de afgelopen jaren nauwlettend in de gaten gehouden en zijn zich scherp bewust van de behoefte aan betere taalfuncties voor het schrijven van meer modulaire JS.

Om deze reden zijn er momenteel voorstellen voor een aantal spannende toevoegingen aan de taal, waaronder flexibele modules die zowel op de client als op de server kunnen werken, een module-lader en meer. In deze sectie laat ik je enkele codevoorbeelden zien van de syntaxis voor modules in ES.next, zodat je een voorsmaakje krijgt van wat komen gaat.

Opmerking: Hoewel Harmony zich nog in de voorstelfase bevindt, kun je dankzij Google's Traceur-compiler al wel (gedeeltelijke) functies van ES.next uitproberen die native ondersteuning bieden voor het schrijven van modulair JavaScript. Om in minder dan een minuut met Traceur aan de slag te kunnen, lees je deze handleiding. Er is ook een JSConf-presentatie over Traceur die de moeite waard is om te bekijken als u meer over het project wilt weten.

Modules met import en export

Als u de hoofdstukken over AMD- en CJS-modules hebt gelezen, bent u wellicht bekend met het concept van moduleafhankelijkheden (import) en module-export (oftewel de openbare API/variabelen die andere modules mogen gebruiken). In ES.next zijn deze concepten op een iets beknoptere manier voorgesteld, waarbij afhankelijkheden worden gespecificeerd met een import sleutelwoord. export verschilt niet veel van wat we zouden verwachten en ik denk dat veel ontwikkelaars de onderstaande code zullen bekijken en het meteen zullen 'snappen'.

  • import declaraties binden de exports van een module als lokale variabelen en kunnen worden hernoemd om naamsbotsingen/conflicten te vermijden.
  • export declaraties verklaren dat een lokale binding van een module extern zichtbaar is, zodat andere modules de exports wel kunnen lezen, maar niet kunnen wijzigen. Interessant is dat modules wel kindermodules kunnen exporteren, maar geen modules die elders gedefinieerd zijn. U kunt ook exports hernoemen zodat hun externe naam verschilt van hun lokale naam.

Modules Loaded From Remote Sources

De modulevoorstellen voorzien ook in modules die op afstand zijn gebaseerd (b.v. een API-wrapper van een derde partij) waardoor het eenvoudig is om modules van externe locaties in te laden. Hier is een voorbeeld van het binnenhalen en gebruiken van de module die we hierboven gedefinieerd hebben:

Module Loader API

De voorgestelde module loader beschrijft een dynamische API voor het laden van modules in zeer gecontroleerde contexten. Ondersteunde handtekeningen op de loader zijn load( url, moduleInstance, error) voor het laden van modules, createModule( object, globalModuleReferences) en anderen. Hier is nog een voorbeeld van het dynamisch inladen van de module die we oorspronkelijk gedefinieerd hebben. Merk op dat in tegenstelling tot het laatste voorbeeld waar we een module van een bron op afstand binnenhaalden, de module loader API beter geschikt is voor dynamische contexten.

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

CommonJS-achtige Modules Voor De Server

Voor ontwikkelaars die server-georiënteerd zijn, is het module-systeem dat voor ES.next wordt voorgesteld niet alleen beperkt tot het bekijken van modules in de browser. Hieronder zie je als voorbeeld een CJS-achtige module voorgesteld voor gebruik op de server:

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

Het begrip klasse is altijd omstreden geweest bij puristen en tot nu toe hebben we het gered door ofwel terug te vallen op JavaScript’s prototypische aard of door gebruik te maken van raamwerken of abstracties die de mogelijkheid bieden om klasse-definities te gebruiken in een vorm die ontleedt naar hetzelfde prototypische gedrag.

In Harmony komen klassen als onderdeel van de taal, samen met constructors en (eindelijk) enig gevoel van echte privacy. In de volgende voorbeelden heb ik wat inline commentaar opgenomen om je te helpen begrijpen hoe klassen zijn opgebouwd, maar het zal je misschien ook opvallen dat het woord “functie” hier ontbreekt. Dit is geen typefout: TC39 heeft een bewuste poging gedaan om ons misbruik van het function sleutelwoord voor alles te verminderen en de hoop is dat dit zal helpen om de manier waarop we code schrijven te vereenvoudigen.

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.

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.