JavaScript was nooit bedoeld om te worden wat het nu is. In 1995 ging Brendan Eich bij Netscape zitten en werkte hij in tien dagen een prototype uit. Tien dagen. Dat is nauwelijks genoeg tijd om een fatsoenlijke specificatie te schrijven, laat staan een programmeertaal te ontwerpen die bedoeld is om miljarden apparaten aan te sturen. Het resultaat was een taal met eigenaardigheden waar ontwikkelaars bijna drie decennia later nog steeds over struikelen. Toch werd diezelfde haastige creatie de meest wijdverspreide runtime in de geschiedenis van software. Het won niet omdat het elegant was, maar omdat het in de browser werd geleverd op het exacte moment dat het web het nodig had.
Geboren in de haast
De browseroorlogen van het midden van de jaren negentig waren geen vriendelijke competitie. Netscape had een lichtgewicht scriptingtaal nodig die naast Java kon draaien in de Navigator-browser. Eich kreeg de taak om iets te bouwen dat genoeg op Java leek om de directie tevreden te stellen, maar dat simpel genoeg was voor niet-programmeurs om in webpagina's te plakken. De deadline was absurd. Hij produceerde Mocha, dat al snel werd omgedoopt tot LiveScript, en uiteindelijk JavaScript als een marketingtruc om in te haken op de populariteit van Java.
Die gehaaste geboorte liet blijvende littekens achter. Type coercion verwart nieuwkomers nog steeds wanneer de plus-operator strings en getallen zonder waarschuwing samenvoegt. typeof null geeft "object" terug vanwege een bug in de oorspronkelijke implementatie die niemand durft te repareren uit angst het web kapot te maken. Automatic semicolon insertion veroorzaakt stille fouten. Variabelen die zijn verklaard met var lekken scope op manieren die onvoorspelbaar aanvoelen. Dit zijn geen abstracte ontwerpfouten. Het zijn dagelijkse frustraties die rechtstreeks terug te voeren zijn op een sprint van twee weken in mei 1995.
Een vreemde afstamming
Kijk je goed naar JavaScript, dan zie je drie duidelijke stambomen die aan elkaar zijn genaaid. De syntaxis leent zwaar van Java. Krulhaken, if-statements en for-loops geven een vertrouwde vorm aan iedereen die afkomstig is uit C-stijl talen. Maar onder dat oppervlak is het gedrag totaal anders.
Het echte computationele hart van de taal komt van Scheme, een dialect van Lisp. Hier komt JavaScript zijn first-class functions vandaan, wat betekent dat functies als argumenten kunnen worden doorgegeven, kunnen worden geretourneerd door andere functies en aan variabelen kunnen worden toegewezen. Het gaf ons ook closures, waardoor een innerlijke functie toegang heeft tot de scope van een buitenste functie, zelfs nadat die buitenste functie is voltooid. Als je ooit een callback hebt geschreven of een event listener hebt toegevoegd, heb je DNA gebruikt dat is geërfd van Scheme.
Dan is er het objectmodel, dat afkomstig is van Self. In plaats van klassieke overerving met rigide klassen, gebruikt JavaScript prototypes. Een object kan direct naar een ander object linken en het zoeken naar eigenschappen naar boven delegeren. Je kunt een object maken met Object.create en ketens opbouwen zonder ooit een klasse te definiëren. Moderne JavaScript heeft het class-trefwoord toegevoegd, maar dat is voornamelijk syntactic sugar over deze onderliggende prototype-machinerie.
Van paginadecoratie naar een serieus hulpmiddel
De eerste jaren deed JavaScript kleine klusjes. Het valideerde formulierinvoer voordat een verzending de server bereikte. Het wisselde afbeeldingen bij een mouse hover. Het was een speeltje, geen hulpmiddel. Browserimplementaties waren inconsistent, waardoor ontwikkelaars vaak verschillende codepaden schreven voor Netscape en Internet Explorer.
Standaardisatie via ECMAScript veranderde die koers. De specificatie gaf browserleveranciers een gemeenschappelijk doel om te implementeren, waardoor de ergste incompatibiliteiten langzaam werden gladgestreken. Toen kwam Ajax.
Ajax, een afkorting voor Asynchronous JavaScript and XML, was geen enkele nieuwe technologie, maar een patroon dat bestaande onderdelen combineerde. Het cruciale ingrediënt was het XMLHttpRequest-object, waarmee de browser op de achtergrond om gegevens van de server kon vragen zonder de hele pagina opnieuw te laden. Toen Google Maps lanceerde in 2005 en Gmail in 2004, ervoeren gebruikers plotseling een desktop-achtige responsiviteit binnen een browsertabblad. Webpagina's werden applicaties. JavaScript was niet langer een garnering. Het was het hoofdgerecht.
Snelheid en ambitie
Ruwe prestaties waren vroeger de grootste grap van JavaScript. Vroege interpreters waren traag. Toen bracht Google in 2008 de V8-engine uit samen met Chrome, hield de grap op grappig te zijn. V8 introduceerde just-in-time-compilatie, waarbij JavaScript tijdens runtime in machinecode wordt vertaald in plaats van regel voor regel te interpreteren. Het introduceerde hidden classes en inline caching om toegang tot eigenschappen snel te maken, zelfs op dynamische objecten. Andere browsers reageerden met hun eigen hogesnelheidsengines, en de taal werd plotseling snel genoeg voor echte berekeningen.
Die snelheid maakte de volgende verschuiving mogelijk. Ryan Dahl bracht Node.js uit in 2009, waarbij hij V8 uit de browser haalde en het verpakte in een event-driven, non-blocking input-output-model. Webservers maakten vroeger voor elke inkomende aanvraag een nieuwe thread aan, wat bij zware gelijktijdigheid tot instorting leidde. Node.js verwerkte tienduizenden gelijktijdige verbindingen op een enkele thread met behulp van een event loop en asynchrone callbacks. JavaScript verspreidde zich van de client naar de server, naar build-tools en uiteindelijk naar alles wat er was.
De taal die overal is
Nu draait JavaScript op plekken die de maker ervan nooit had kunnen voorzien.
Aan de frontend vormen React en Vue de manier waarop moderne interfaces worden gebouwd. Componenten worden bijgewerkt als reactie op state-wijzigingen, zonder dat de browser kostbare volledige pagina-reloads hoeft uit te voeren. Aan de backend drijft Node.js API's en real-time services aan, terwijl nieuwere runtimes zoals Bun experimenteren met snellere package management en ingebouwde bundling.
React Native vertaalt JavaScript-code naar native platform-views, waardoor teams mobiele applicaties voor iOS en Android kunnen uitbrengen zonder twee volledig gescheiden codebases in Swift en Kotlin te hoeven onderhouden. Electron verpakt webtechnologieën in een Chromium-shell om desktopsoftware te bouwen, wat de reden is dat zowel Slack als Visual Studio Code op je laptop terechtkomen. De taal bevindt zich zelfs in cloud functions en edge computing workers, waarbij logica wordt uitgevoerd op slechts enkele milliseconden afstand van de eindgebruiker via gedistribueerde netwerken.
De paradox van overvloed
Alomtegenwoordigheid heeft een prijs. Het ecosysteem is enorm, en die omvang wekt angst op. Er verschijnt een nieuwe build-tool voordat je klaar bent met het configureren van de vorige. Frameworks stijgen en dalen in populariteit op tijdlijnen die aanvoelen als seizoenen. Je kunt bijna elk probleem oplossen zonder de JavaScript-wereld te verlaten, maar je moet eerst kiezen uit een dozijn zeer eigenzinnige oplossingen. Dependency trees worden diep en broos. Een package voor het links vullen van arrays kan duizenden downstream-projecten kapotmaken wanneer deze uit de registry verdwijnt.
Niets hiervan is toevallig. JavaScript is rommelig omdat het web rommelig is. Het is een organische laagjeskoek van backward compatibility, overhaaste standaarden en concurrerende belangen bij implementaties. Toch is diezelfde chaos de reden dat de taal krachtig is. Het web is overal, en omdat JavaScript standaard in elke browser leeft, is het het dichtstbijzijnde dat we hebben van een universele runtime.
Het is lang geleden gestopt met het zijn van een louter scriptingtaal. JavaScript is nu een wereldwijd platform voor software, gebouwd in tien dagen, bij elkaar gehouden door het onzichtbare contract dat het web niet mag breken wat er eerder was. Leer de littekens kennen naast de sterke punten, en je leert de geschiedenis van het moderne internet zelf.
