All optimising Javascript runtimes use “shapes” (SpiderMonkey term) or “hidden classes” so that instead of objects being treated as the dictionaries or hashmaps they can instead be treated like fixed regular structs. This makes property lookups much more efficient when you have many objects using the same shape. But notably, simply having the same properties does not ensure that the hidden shape definition will be the same – adding properties to an object in a different order will result in different shapes. This article by Mathias Bynens is a great introduction to the topic.
Beyond the trivial things like not needlessly setting properties in a random order, what principles should I be following to write code that will use shapes in a highly performant way?
- Is it important to ensure that all entries in an array have the same shape?
- Is
delete
a performance killer compared to setting a property tonull
? And therefore that Typescript’s optional properties should be avoided in hot code? - Is setting lots of properties one by one in a class constructor worse than setting many properties at once in an object literal? If so, how should classes ideally be used?
- Are there any other general principles for shapes that don’t just boil down to being careful when adding/deleting properties?
It may be that the performance hit of using shapes inoptimally is not really so severe; rather shapes are just something that it helps to develop a sixth sense for, to develop in such a way that you use regular object definitions as much as possible. But if there are some general guidelines it would be great to learn them.
7
Don’t write JavaScript to leverage runtime optimizations.
Once you legitimately need to entertain runtime-level optimizations to solve a performance problem, it is time to seriously consider whether or not JavaScript is the right tool for the job.
Optimizing code is an immensely complicated topic, which is further complicated by the dynamic nature of JavaScript. Each browser vendor — well, each JavaScript runtime — is free to optimize your code in its own way. The premise of this question seems to assume there is an agreed-upon list of optimizations that browser vendors choose from. Some common patterns might have developed over the years, but optimizing your JavaScript code at this level is like trying to write C, C++ or some other compiled language to leverage the fastest possible compiler optimizations.
In other words: don’t write JavaScript code to leverage specific runtime optimizations. These can change over time, and will vary based on browser, and even between browser versions.
In my experience, frontend performance issues rarely involved brute JavaScript runtime performance. Upon profiling applications, the worst offenders were often:
- Scripts, images or other media that delayed time to interactive.
- Inefficient DOM manipulation that triggered multiple re-renders.
- Very large document trees (e.g. “tens of thousands of HTML tags”) that were time consuming to manipulate even once. Restructuring and re-rendering a huge document tree can be a performance killer.
- Slow background HTTP requests to fetch information dynamically for the page.
- Flash animations.
- Ok, so this isn’t relevant anymore, but back in the day this could consume more CPU usage than training ChatGPT.
- Ok, even that is sarcastic, but boy it felt that way at the time.
- Ok, so this isn’t relevant anymore, but back in the day this could consume more CPU usage than training ChatGPT.
- Advertisements. See bullet point 1. Many ads block the page as it renders, thus delaying Time to Interactive.
I cannot think of a single instance where “writing optimized JavaScript code” fixed a legitimate, annoying, and measurable performance issue. Writing JavaScript to leverage specific runtime optimizations falls in to the “premature optimizations are the root of all evil” category.
That being said, these general rules don’t cover every possible use case. A few I can think of are complex charts and graphs, and browser-based games.
Your efforts as a software engineer are better spent ensuring that rendering a complex chart or graphic does not freeze the user interface rather than making it quick. Focus on user experience and feedback rather than quickness.
Games can also be a challenge due to the processing burden required over a very short period of time. In that case, I would look into writing the application in Web Assembly. Many popular non-web languages can be transpiled to Web Assembly.
Now the only question becomes, how do I write runtime optimized Web Assembly? 🤔 — scratch that. Go back to the top of this answer, and replace “JavaScript” with “Web Assembly” and you’ll have the answer to that question, too: don’t.
12
I found this post : https://romgrk.com/posts/optimizing-javascript/
Which lists a bunch of interesting optimisations for javascripters in 2024. The best thing is it includes benchmarks for each!
“Avoid different shapes” is included and the performance hit is large (“Percentage results represent the number of operations completed within 1s, divided by the number of operations of the highest scoring case. Higher is better.”)
It also suggests
“Use eval”
and
“Avoid map filter reduce”
I think this is my new favorite page on the internet and I will be optimising all my javascript code in future, mainly to troll front end devs 🙂
0