- Gebruik class in plaats van objecten. Initialiseer velden meteen in de constructor, ook de velden die undefined zijn. Het idee is om classes stabiel te houden en niet van vorm te laten veranderen, want een verandering van vorm maakt een nieuw JIT definitie voor de class.
- Idem aan vorige punt, gebruik geen types door elkaar. Als je een veld hebt wat nu een number is en straks ineens een string, dan maak je weer een nieuwe JIT definitie. Probeer dat allemaal te voorkomen en kies een enkele consistente representatie voor een veld.
- Idem aan vorige punt, gebruik nooit delete maar zet een veld naar undefined.
- Gebruik liever een class dan een anoniem type object. Ja, een object in elkaar flansen is minder schrijfwerk, maar het is ook minder performant omdat ieder anoniem object mogelijk een eigen JIT definitie krijgt. Hoe beter de JIT je code snapt, hoe meer het kan optimaliseren.
Deze punten zijn grotendeels achterhaald. Moderne engines zoals de V8 engine in Chrome/ium, waar ook Node.js en Electron op draaien maken gebruik van zgn.
hidden classes voor anonieme objecten die o.a. middels iets wat een
TransitionArray bijhoudt hoe de ene
object shape in de andere overvloeit bij het wijzigen v/h type v/d waarde achter een bestaande key, bij het toevoegen van een key, of bij het verwijderen van een key.
Blijf met je tengels van prototype af.
Klopt in zoverre dat je niet de prototype definities van bestaande data types aan moet gaan passen die onderdeel van de taal basis of gemeenschappelijk zaken zoals de DOM zijn. Dit kan namelijk verstrekkende de-optimalisatiegevolgen hebben wanneer je functies raakt die speciale optimalisaties in de runtime gekregen hebben.
Een goed voorbeeld hiervan is de implementatie van Symbol.iterator op itereerbare types aanpassen. Als je dat doet verdwijnen ineens een heleboel optimalisatie-mogelijkheden omdat er niet meer aangenomen kan worden dat iteratie op de normale manier werkt zoals bij bijv. een array hoort. En dus kan er geen optimale machine code representatie van gemaakt worden.
Maar verder is er helemaal niets mis met het aanpassen van prototypes binnen de data types die je zelf aanmaakt. Het is een heel goede manier om mixins en aspect-based programming te bezigen binnen JavaScript.
Geen arguments gebruiken -> function aap(...a) is heel langzaam.
Een
rest argument is niet hetzelfde als de ambiente arguments variabele.
Rest arguments zijn in elk geval in V8 onderdeel van de type signature van de functie en vormen een onderdeel van de object shape van de functie. Deze kan in V8 monomorphic, polymorphic, of megamorphic zijn: dwz. aangeroepen worden in slechts één parameter variatie, aangeroepen worden in minder dan N verschillende variaties aan cardinaliteit en type van parameters, of aangeroepen worden in meer dan N verschillende variaties. Pas bij meer dan N vindt er een deoptimalisatie plaats en zal er geen efficiente machine code voor de functie aanroep gegenereerd kunnen worden.
Generator functies zijn veel sneller dan meerdere .map, .filter, .reduce, enz.
Omdat je geen interne tussentijdse collecties aan hoeft te maken.
Maar als het aankomt op bijv. enkel itereren, is
.forEach tegenwoordig even snel als
for-in,
for-of en de klassieke
for loop. De performance verbeteringen daarvoor zijn al
minstens sinds 2017 gemeengoed.
Schrijven naar properties triggered een paint, dus gebruik variables om eerst alles door te rekenen voordat je de uiteindelijke waarde naar de DOM property schrijft.
Dit klopt niet. Schrijven naar properties
kan een wijziging bewerkstelligen die een relayout of repaint nodig heeft. Maar wanneer dit nodig is wordt deze uitgesteld tot een later moment, efficient in de rendering pipeline, zodat meer wijzigingen samengevoegd in één batch ineens doorgevoerd kunnen worden.
Dat gaat niet als je tussentijds properties uitleest die afhankelijk zijn van layout. Wat je dus moet vermijden is het bijwerken van de DOM en vervolgens het meteen teruglezen van gegevens uit DOM, want dan draai je die optimalisatie de nek om en veroorzaak je een zgn
synchronous layout pass. Klassiek voorbeeld is de hoogte aanpassen en vervolgens de
clientHeight property terug uit lezen.
Reads en writes hoor je gescheiden te doen in batches, bij voorkeur mbv de
requestAnimationFrame functie om deze op het optimale moment binnen de relayout cycle door te voeren zodat er slechts
één asynchroon uitgevoerde re-layout nodig is die alle wijzigingen in één keer als batch kan doorvoeren.
Gebruik nooit animaties geschreven in JavaScript (want paint en layout events). Gebruik alleen CSS voor animaties
Als je requestAnimationFrame op de juiste manier gebruikt is dat qua gegenereerde paint en layout events even efficient als een CSS animatie. Belangrijker is dat je enkel eigenschappen manipuleert die middels de
compositor afgehandeld kunnen worden en niet een re-layout of repaint nodig hebben. Animeer dus vooral zaken als
opacity of
transform en vooral niet zaken als
width of
height.
Gebruik nooit innerHTML: werk netjes met nodes en ga er slim mee om.
innerHTML is juist vele malen sneller omdat het in één klap de hele node tree aan kan maken zonder elke keer voor het aanmaken van elke DOM node tussen native object representatie en JS representatie te moeten call-marshallen.
adjacentHTML is iets wat tijden enkel in Internet Explorer ondersteund was, en waar JS framework auteurs
super blij mee waren als performance verbetering tov via de Node/Element APIs siblings achter moeten voegen.
Render alleen wat nodig is; gebruik licht gewicht placeholders voor dingen die ver uit beeld zijn en toon ze alleen wanneer de gebruik in de buurt komt. Veel DOM nodes (1000+) maakt alles traag.
Dit is achterhaald. Je kunt dit tegenwoordig sturen vanuit CSS middels de
content-visibility property en de
contain property.
[Reactie gewijzigd door R4gnax op 12 november 2025 19:50]