JavaScript-ontwerppatronen

De ultieme gids voor de meest bruikbare ontwerppatronen

UPDATE OPMERKING: het voorbeeld van het proxypatroon bijgewerkt om ES6 proxy en reflect te gebruiken. Afbeeldingen van broncodefragmenten vervangen door GitHub-overzichten.

In dit artikel gaan we het hebben over ontwerppatronen die kunnen en moeten worden gebruikt om betere, onderhoudbare JavaScript-code te schrijven. Ik neem aan dat je een basiskennis hebt van JavaScript en concepten zoals klassen (klassen in JavaScript kunnen lastig zijn), objecten, prototypische overerving, sluitingen, enz.

Dit artikel is als geheel lang gelezen vanwege de aard van het onderwerp, dus ik heb geprobeerd de secties op zichzelf te houden. Zodat u als lezer specifieke onderdelen (of in dit geval specifieke patronen) kunt kiezen en negeren waar u niet in geïnteresseerd bent of waar u goed mee vertrouwd bent. Laten we nu beginnen.

Opmerking: Broncode voor de implementatie van alle ontwerppatronen die hier worden uitgelegd, staat op GitHub.

Invoering

We schrijven code om problemen op te lossen. Deze problemen hebben meestal veel overeenkomsten en als we ze proberen op te lossen, zien we verschillende gemeenschappelijke patronen. Hier komen ontwerppatronen binnen.

Een ontwerppatroon is een term die wordt gebruikt in software-engineering voor een algemene, herbruikbare oplossing voor een veel voorkomend probleem bij softwareontwerp.

Het onderliggende concept van ontwerppatronen bestaat al sinds het begin in de software-engineeringindustrie, maar ze waren niet echt zo geformaliseerd. Design Patterns: Elements Of Reusable Object-Oriented Software geschreven door Erich Gamma, Richard Helm, Ralph Johnson en John Vlissides - de beroemde Gang of Four (GoF) - waren van groot belang bij het doorvoeren van het geformaliseerde concept van ontwerppatronen in software engineering. Nu zijn ontwerppatronen een essentieel onderdeel van softwareontwikkeling en dat is al lang zo.

Er waren 23 ontwerppatronen geïntroduceerd in het originele boek.

De klassieke 23 patronen geïntroduceerd door GoF

Ontwerppatronen zijn om verschillende redenen gunstig. Het zijn bewezen oplossingen die veteranen uit de industrie hebben geprobeerd en getest. Het zijn solide benaderingen die problemen op een breed geaccepteerde manier oplossen en de ervaring en inzichten weerspiegelen van de toonaangevende ontwikkelaars die deze hebben helpen definiëren. Patronen maken uw code ook meer herbruikbaar en leesbaar terwijl het ontwikkelingsproces enorm wordt versneld.

Ontwerppatronen zijn nog lang niet alle oplossingen. Ze bieden ons alleen benaderingen of schema's om een ​​probleem op te lossen.

Opmerking: In dit artikel zullen we het vooral hebben over ontwerppatronen vanuit een objectgeoriënteerd gezichtspunt en in de context van hun bruikbaarheid in moderne JavaScript. Dat is de reden waarom veel klassieke patronen uit GoF kunnen worden weggelaten, en sommige moderne patronen uit bronnen zoals Addy Osmani's Learn JavaScript-ontwerppatronen zullen worden opgenomen. De voorbeelden worden eenvoudig gehouden voor een beter begrip en zijn daarom niet de meest geoptimaliseerde implementatie van hun respectieve ontwerppatronen.

Categorieën ontwerppatronen

Ontwerppatronen worden meestal onderverdeeld in drie grote groepen.

Creatieve ontwerppatronen

Zoals de naam al doet vermoeden, zijn deze patronen bedoeld voor het hanteren van objectcreatieve mechanismen. Een creatief ontwerppatroon lost in principe een probleem op door het creatieproces van een object te besturen.

We zullen de volgende patronen in detail bespreken: Constructorpatroon, Fabriekspatroon, Prototypepatroon en Singletonpatroon.

Structurele ontwerppatronen

Deze patronen hebben betrekking op de samenstelling van klassen en objecten. Ze helpen bij het structureren of herstructureren van een of meer onderdelen zonder het hele systeem te beïnvloeden. Met andere woorden, ze helpen bij het verkrijgen van nieuwe functionaliteiten zonder met de bestaande te knoeien.

We zullen de volgende patronen in detail bespreken: adapterpatroon, composietpatroon, decorateurpatroon, gevelpatroon, vlieggewichtpatroon en proxypatroon.

Gedragspatronen

Deze patronen hebben betrekking op het verbeteren van de communicatie tussen verschillende objecten.

We zullen de volgende patronen in detail bespreken: Chain of Responsibility Pattern, Command Pattern, Iterator Pattern, Mediator Pattern, Observer Pattern, State Pattern, Strategy Pattern en Template Pattern.

Constructor patroon

Dit is een op klassen gebaseerd creatief ontwerppatroon. Constructors zijn speciale functies die kunnen worden gebruikt om nieuwe objecten te instantiëren met methoden en eigenschappen die door die functie worden gedefinieerd.

Het is niet een van de klassieke ontwerppatronen. In feite is het meer een basistaalconstructie dan een patroon in de meeste objectgeoriënteerde talen. Maar in JavaScript kunnen objecten direct worden gemaakt zonder constructorfuncties of "klassedefinitie". Daarom denk ik dat het belangrijk is om de basis te leggen voor andere patronen die bij deze eenvoudige komen.

Constructorpatroon is een van de meest gebruikte patronen in JavaScript voor het maken van nieuwe objecten van een bepaald type.

In dit voorbeeld definiëren we een Hero-klasse met attributen zoals name en specialAbility en methoden zoals getDetails. Vervolgens instantiëren we een object IronMan door de constructormethode aan te roepen met het nieuwe sleutelwoord dat de waarden voor de respectieve attributen als argumenten doorgeeft.

Fabriek patroon

Fabriekspatroon is een ander op klassen gebaseerd creatiepatroon. Hierin bieden we een generieke interface die de verantwoordelijkheid voor objectinstantie delegeert aan zijn subklassen.

Dit patroon wordt vaak gebruikt wanneer we verzamelingen objecten moeten beheren of manipuleren die verschillend zijn en toch veel vergelijkbare kenmerken hebben.

In dit voorbeeld maken we een fabrieksklasse met de naam BallFactory die een methode heeft die parameters opneemt en, afhankelijk van de parameters, de object instantiëringsverantwoordelijkheid delegeert aan de respectieve klasse. Als de parameter type "football" of "football" is, wordt objectminstiatie afgehandeld door de Football-klasse, maar als het "basketbal" is, wordt object instantiation afgehandeld door de Basketball-klasse.

Prototype patroon

Dit patroon is een objectgebaseerd ontwerppatroon. Hierin gebruiken we een soort 'skelet' van een bestaand object om nieuwe objecten te maken of te instantiëren.

Dit patroon is specifiek belangrijk en voordelig voor JavaScript omdat het gebruik maakt van prototypische overerving in plaats van een klassieke objectgeoriënteerde overerving. Het speelt dus in op de kracht van JavaScript en heeft native ondersteuning.

In dit voorbeeld hebben we een auto-object dat we gebruiken als het prototype om een ​​ander object myCar te maken met de functie Object.create van JavaScript en een extra eigenaar van het object te definiëren.

Singleton patroon

Singleton is een speciaal creatief ontwerppatroon waarin slechts één instantie van een klasse kan bestaan. Het werkt als volgt - als er geen instantie van de singleton-klasse bestaat, wordt er een nieuwe instantie gemaakt en geretourneerd, maar als er al een instantie bestaat, wordt de verwijzing naar de bestaande instantie geretourneerd.

Een perfect real-life voorbeeld zou dat zijn van mangoest (de beroemde Oode-bibliotheek van Node.js voor MongoDB). Het maakt gebruik van het singleton-patroon.

In dit voorbeeld hebben we een databaseklasse die een singleton is. Eerst maken we een objectmongo door de nieuwe operator te gebruiken om de databaseklasseconstructor aan te roepen. Deze keer wordt een object geïnstantieerd omdat er nog geen bestaat. De tweede keer, wanneer we het mysql-object maken, wordt er geen nieuw object geïnstantieerd, maar in plaats daarvan wordt de verwijzing naar het object dat eerder werd geïnstantieerd, d.w.z. het mongo-object, geretourneerd.

Adapterpatroon

Dit is een structureel patroon waarbij de interface van de ene klasse wordt vertaald in een andere. Met dit patroon kunnen klassen samenwerken die anders niet konden vanwege incompatibele interfaces.

Dit patroon wordt vaak gebruikt om wrappers te maken voor nieuwe refactored API's, zodat andere bestaande oude API's er nog steeds mee kunnen werken. Dit wordt meestal gedaan wanneer nieuwe implementaties of coderefactoring (gedaan om redenen zoals prestatiewinst) resulteren in een andere openbare API, terwijl de andere delen van het systeem nog steeds de oude API gebruiken en moeten worden aangepast om samen te werken.

In dit voorbeeld hebben we een oude API, d.w.z. OldCalculator-klasse, en een nieuwe API, d.w.z. NewCalculator-klasse. De OldCalculator-klasse biedt een bewerkingsmethode voor zowel optellen als aftrekken, terwijl de NewCalculator afzonderlijke methoden biedt voor optellen en aftrekken. De adapterklasse CalcAdapter wikkelt de NewCalculator om de bedieningsmethode toe te voegen aan de publiekgerichte API terwijl de eigen implementatie voor optellen en aftrekken onder de motorkap wordt gebruikt.

Composiet patroon

Dit is een structureel ontwerppatroon dat objecten samenstelt in boomachtige structuren om hiërarchieën van hele delen weer te geven. In dit patroon kan elke knoop in de boomachtige structuur een afzonderlijk object of een samengestelde verzameling objecten zijn. Hoe dan ook, elke knoop wordt uniform behandeld.

Een menustructuur met meerdere niveaus

Het is een beetje ingewikkeld om dit patroon te visualiseren. De eenvoudigste manier om hierover na te denken is met het voorbeeld van een menu met meerdere niveaus. Elk knooppunt kan een afzonderlijke optie zijn, of het kan een menu zelf zijn, dat meerdere opties als kind heeft. Een knoopcomponent met kinderen is een samengestelde component, terwijl een knoopcomponent zonder enig kind een bladcomponent is.

In dit voorbeeld maken we een basisklasse van Component die de benodigde gemeenschappelijke functionaliteiten implementeert en de andere benodigde methoden abstraheert. De basisklasse heeft ook een statische methode die gebruik maakt van recursie om een ​​samengestelde boomstructuur te doorkruisen die met zijn subklassen is gemaakt. Vervolgens maken we twee subklassen die de basisklasse uitbreiden - Leaf zonder kinderen en Composite met kinderen - en hebben daarom methoden voor het toevoegen, zoeken en verwijderen van kindfunctionaliteiten. De twee subklassen worden gebruikt om een ​​samengestelde structuur te maken - in dit geval een boom.

Decorateur Patroon

Dit is ook een structureel ontwerppatroon dat zich richt op het vermogen om dynamisch gedrag of functionaliteiten aan bestaande klassen toe te voegen. Het is een ander uitvoerbaar alternatief voor subklasse.

Het gedrag van het decoratortype is zeer eenvoudig te implementeren in JavaScript, omdat we met JavaScript methoden en eigenschappen kunnen toevoegen om dynamisch bezwaar te maken. De eenvoudigste aanpak zou zijn om gewoon een eigenschap aan een object toe te voegen, maar deze is niet efficiënt herbruikbaar.

Er is zelfs een voorstel om decorateurs toe te voegen aan de JavaScript-taal. Bekijk het bericht van Addy Osmani over decorateurs in JavaScript.

Als je meer wilt lezen over het voorstel zelf, voel je vrij.

In dit voorbeeld maken we een klasse Book. We creëren verder twee decoratiefuncties die een boekobject accepteren en een "gedecoreerd" boekobject retourneren - giftWrap dat een nieuw kenmerk en een nieuwe functie toevoegt en hardbindBook dat een nieuw kenmerk toevoegt en de waarde van een bestaand kenmerk bewerkt.

Gevelpatroon

Dit is een structureel ontwerppatroon dat veel wordt gebruikt in de JavaScript-bibliotheken. Het wordt gebruikt om een ​​uniforme en eenvoudigere, publiekgerichte interface te bieden voor gebruiksgemak dat de complexiteit van zijn bestaande subsystemen of subklassen afschermt.

Het gebruik van dit patroon is heel gebruikelijk in bibliotheken zoals jQuery.

In dit voorbeeld maken we een publiekgerichte API met de klasse ComplaintRegistry. Er wordt slechts één methode weergegeven die door de client moet worden gebruikt, namelijk registerComplaint. Het verwerkt intern instantiërende vereiste objecten van ProductComplaint of ServiceComplaint op basis van het argument type. Het behandelt ook alle andere complexe functionaliteiten, zoals het genereren van een unieke ID, het opslaan van de klacht in het geheugen, enz. Maar al deze complexiteiten worden verborgen met behulp van het gevelpatroon.

Vlieggewicht patroon

Dit is een structureel ontwerppatroon gericht op efficiënte gegevensuitwisseling via fijnkorrelige objecten. Het wordt gebruikt voor efficiëntie en geheugenbehoud.

Dit patroon kan voor elke vorm van caching worden gebruikt. Moderne browsers gebruiken zelfs een variant van een vlieggewichtpatroon om te voorkomen dat dezelfde afbeeldingen tweemaal worden geladen.

In dit voorbeeld maken we een fijnkorrelige vlieggewichtklasse Icecream voor het delen van gegevens met betrekking tot ijssmaken en een fabrieksklasse IcecreamFactory om die vlieggewichtobjecten te maken. Voor geheugenbehoud worden de objecten gerecycled als hetzelfde object twee keer wordt geïnstantieerd. Dit is een eenvoudig voorbeeld van de implementatie van vlieggewicht.

Proxy-patroon

Dit is een structureel ontwerppatroon dat zich precies gedraagt ​​zoals de naam al doet vermoeden. Het fungeert als een surrogaat of tijdelijke aanduiding voor een ander object om de toegang ertoe te beheren.

Het wordt meestal gebruikt in situaties waarin een doelobject onder beperkingen staat en mogelijk niet in staat is om al zijn verantwoordelijkheden efficiënt af te handelen. Een proxy biedt in dit geval meestal dezelfde interface voor de client en voegt een niveau van richting toe om gecontroleerde toegang tot het doelobject te ondersteunen om onnodige druk erop te voorkomen.

Het proxy-patroon kan erg handig zijn bij het werken met netwerkaanvraag-zware applicaties om onnodige of redundante netwerkverzoeken te voorkomen.

In dit voorbeeld zullen we twee nieuwe ES6-functies gebruiken, Proxy en Reflect. Een Proxy-object wordt gebruikt om aangepast gedrag te definiëren voor fundamentele bewerkingen van een JavaScript-object (onthoud, functie en arrays zijn ook object in JavaScript). Het is een constructormethode die kan worden gebruikt om een ​​Proxy-object te maken. Het accepteert een doelobject dat moet worden benaderd en een handlerobject dat de benodigde aanpassing zal definiëren. Met het handler-object kunnen enkele trap-functies worden gedefinieerd, zoals ophalen, instellen, hebben, toepassen, enz. Die worden gebruikt om aangepast gedrag toe te voegen dat aan hun gebruik is gekoppeld. Reflect daarentegen is een ingebouwd object dat vergelijkbare methoden biedt die door het handlerobject van Proxy op zichzelf worden ondersteund als statische methoden. Het is geen constructeur; de statische methoden worden gebruikt voor onderscheppende JavaScript-bewerkingen.

Nu maken we een functie die kan worden gezien als een netwerkverzoek. We hebben het netwerkFetch genoemd. Het accepteert een URL en reageert dienovereenkomstig. We willen een proxy implementeren waarbij we alleen de reactie van het netwerk krijgen als deze niet beschikbaar is in onze cache. Anders retourneren we gewoon een reactie van de cache.

De globale variabele in de cache slaat onze in de cache opgeslagen antwoorden op. We maken een proxy met de naam proxiedNetworkFetch met ons oorspronkelijke netwerkFetch als het doel en gebruiken de methode toepassen in ons handlerobject om de functieoproep te proxyen. De methode Toepassen wordt doorgegeven aan het doelobject zelf. Deze waarde als thisArg en de argumenten worden doorgegeven in een array-achtige structuur args.

We controleren of het doorgegeven url-argument zich in de cache bevindt. Als het in de cache bestaat, retourneren we het antwoord van daar, zonder de oorspronkelijke doelfunctie aan te roepen. Als dit niet het geval is, gebruiken we de methode Reflect.apply om de targetfunctie met dezeArg aan te roepen (hoewel het hier in ons geval niet van betekenis is) en de argumenten die zijn doorgegeven.

Keten van verantwoordelijkheidspatroon

Dit is een gedragspatroon dat een ketting van losjes gekoppelde objecten biedt. Elk van deze objecten kan ervoor kiezen om op te treden of het verzoek van de klant af te handelen.

Een goed voorbeeld van het keten van verantwoordelijkheidspatroon is het borrelen van een gebeurtenis in DOM waarbij een gebeurtenis zich voortplant via een reeks geneste DOM-elementen, waarvan er een een "gebeurtenis-luisteraar" kan hebben om naar de gebeurtenis te luisteren en ernaar te handelen.

In dit voorbeeld maken we een klasse CumulativeSum, die kan worden geïnstantieerd met een optionele initialValue. Het heeft een methode add die de doorgegeven waarde toevoegt aan het attribuut sum van het object en het object zelf retourneert voor het koppelen van aanroepen van add-methodes.

Dit is een gebruikelijk patroon dat ook in jQuery te zien is, waarbij bijna elke methode-aanroep van een jQuery-object een jQuery-object retourneert zodat methode-aanroepen aan elkaar kunnen worden gekoppeld.

Opdrachtpatroon

Dit is een gedragspatroon dat gericht is op het inkapselen van acties of bewerkingen als objecten. Dit patroon maakt losse koppeling van systemen en klassen mogelijk door de objecten die om een ​​bewerking vragen of een methode aanroepen te scheiden van de objecten die de daadwerkelijke implementatie uitvoeren of verwerken.

De klembordinteractie-API lijkt enigszins op het opdrachtpatroon. Als u een Redux-gebruiker bent, bent u het opdrachtpatroon al tegengekomen. De acties die de geweldige tijdreizen-foutopsporingsfunctie mogelijk maken, zijn niets anders dan ingekapselde bewerkingen die kunnen worden gevolgd om bewerkingen opnieuw uit te voeren of ongedaan te maken. Vandaar dat tijdreizen mogelijk werd gemaakt.

In dit voorbeeld hebben we een klasse met de naam SpecialMath die meerdere methoden heeft en een Command-klasse die opdrachten inkapselt die moeten worden uitgevoerd op het onderwerp, d.w.z. een object van de SpecialMath-klasse. De Command-klasse houdt ook alle uitgevoerde opdrachten bij, die kunnen worden gebruikt om de functionaliteit uit te breiden met bewerkingen voor ongedaan maken en opnieuw uitvoeren.

Iterator patroon

Het is een gedragspatroon dat een manier biedt om achtereenvolgens toegang te krijgen tot de elementen van een geaggregeerd object zonder de onderliggende weergave ervan bloot te leggen.

Iterators hebben een speciaal soort gedrag waarbij we een geordende set waarden een voor een doorlopen door next () aan te roepen tot we het einde bereiken. De introductie van Iterator en generatoren in ES6 maakte de implementatie van het iteratorpatroon uiterst eenvoudig.

We hebben hieronder twee voorbeelden. Ten eerste gebruikt de ene IteratorClass iterator-specificatie, terwijl de andere iteratorUsingGenerator generatorfuncties gebruikt.

De Symbol.iterator (Symbol - een nieuw soort primitief gegevenstype) wordt gebruikt om de standaarditerator voor een object op te geven. Het moet worden gedefinieerd voor een verzameling om de for ... of looping-constructie te kunnen gebruiken. In het eerste voorbeeld definiëren we de constructor om een ​​verzameling gegevens op te slaan en vervolgens Symbol.iterator, die een object retourneert met de volgende methode voor iteratie.

Voor het tweede geval definiëren we een generatorfunctie die een reeks gegevens doorgeeft en de elementen iteratief retourneert met behulp van next en yield. Een generatorfunctie is een speciaal type functie die werkt als een fabriek voor iterators en die expliciet zijn eigen interne status kan behouden en iteratief waarden kan opleveren. Het kan zijn eigen uitvoeringscyclus onderbreken en hervatten.

Bemiddelaar patroon

Het is een gedragspatroon dat aangeeft hoe een set objecten op elkaar inwerken. Het biedt de centrale autoriteit over een groep objecten door losse koppeling te bevorderen, waardoor objecten niet expliciet naar elkaar kunnen verwijzen.

In dit voorbeeld hebben we TrafficTower als bemiddelaar die bepaalt hoe vliegtuigobjecten met elkaar communiceren. Alle Airplane-objecten registreren zichzelf bij een TrafficTower-object en het is het mediator-klasseobject dat afhandelt hoe een Airplane-object coördinaatgegevens van alle andere Airplane-objecten ontvangt.

Waarnemerspatroon

Het is een cruciaal gedragspatroon dat een-op-veel afhankelijkheden tussen objecten definieert, zodat wanneer een object (uitgever) van status verandert, alle andere afhankelijke objecten (abonnees) op de hoogte worden gebracht en automatisch worden bijgewerkt. Dit wordt ook PubSub (publisher / subscribers) of event dispatcher / listeners-patroon genoemd. De uitgever wordt soms het onderwerp genoemd en de abonnees worden soms waarnemers genoemd.

De kans is groot dat u al enigszins bekend bent met dit patroon als u addEventListener of jQuery's hebt gebruikt om een ​​code voor een gelijkmatige verwerking te schrijven. Het heeft ook zijn invloeden in Reactive Programming (denk aan RxJS).

In het voorbeeld maken we een eenvoudige onderwerpklasse met methoden om objecten van de klasse Observer toe te voegen en te verwijderen uit de verzameling abonnees. Ook een brandweer om wijzigingen in het onderwerpklasseobject door te geven aan de ingeschreven waarnemers. De klasse Observer heeft daarentegen zijn interne status en een methode om zijn interne status bij te werken op basis van de wijziging die wordt doorgevoerd door het onderwerp waarop hij is geabonneerd.

Staatspatroon

Het is een gedragspatroon waarmee een object zijn gedrag kan wijzigen op basis van wijzigingen in zijn interne toestand. Het object dat wordt geretourneerd door een statuspatroonklasse lijkt van klasse te veranderen. Het biedt toestandspecifieke logica voor een beperkte set objecten waarin elk objecttype een bepaalde status vertegenwoordigt.

We zullen een eenvoudig voorbeeld van een verkeerslicht nemen om dit patroon te begrijpen. De klasse TrafficLight wijzigt het object dat wordt geretourneerd op basis van de interne status, een object van de klasse Rood, Geel of Groen.

Strategie patroon

Het is een gedragspatroon dat het mogelijk maakt om alternatieve algoritmen voor een bepaalde taak in te kapselen. Het definieert een familie van algoritmen en inkapselt ze zodanig dat ze tijdens runtime uitwisselbaar zijn zonder tussenkomst van de klant of kennis.

In het onderstaande voorbeeld maken we een klasse Commute voor het inkapselen van alle mogelijke strategieën voor woon-werkverkeer. Vervolgens definiëren we drie strategieën, namelijk Bus, PersonalCar en Taxi. Met dit patroon kunnen we de implementatie omwisselen voor gebruik tijdens de reismethode van het object Commute tijdens runtime.

Sjabloonpatroon

Dit is een gedragspatroon gebaseerd op het definiëren van het skelet van het algoritme of de implementatie van een bewerking, maar het uitstellen van enkele stappen naar subklassen. Hiermee kunnen subklassen bepaalde stappen van een algoritme opnieuw definiëren zonder de uiterlijke structuur van het algoritme te wijzigen.

In dit voorbeeld hebben we een Template-klasse Medewerker die de werkmethode gedeeltelijk implementeert. Het is aan de subklassen om de verantwoordelijkheidsmethode te implementeren om het in zijn geheel te laten werken. Vervolgens maken we twee subklassen Developer en Tester die de sjabloonklasse uitbreiden en de vereiste methode implementeren om de implementatiekloof te vullen.

Gevolgtrekking

Ontwerppatronen zijn cruciaal voor software-engineering en kunnen zeer nuttig zijn bij het oplossen van veelvoorkomende problemen. Maar dit is een zeer uitgebreid onderwerp, en het is gewoon niet mogelijk om alles over hen in een kort stuk op te nemen. Daarom heb ik de keuze gemaakt om kort en bondig alleen te praten over degene waarvan ik denk dat ze erg handig kunnen zijn bij het schrijven van moderne JavaScript. Om dieper te duiken, raad ik je aan deze boeken te bekijken:

  1. Design Patterns: Elements Of Reusable Object-Oriented Software van Erich Gamma, Richard Helm, Ralph Johnson en John Vlissides (Gang of Four)
  2. Leer JavaScript-ontwerppatronen door Addy Osmani
  3. JavaScript-patronen door Stoyan Stefanov