Helpers that compile, strings that stay small, and events that fire fast
Scripts run faster and hold less memory. Helper functions that call other helpers, optional and rest parameters, objects made in a loop, map and forEach callbacks and Array.from now run in compiled code, most of them ahead of the reference engine. Building a long string holds up to thirty times less memory, closures and small objects take less, and a page with thousands of event listeners handles an event about five times faster. A script that builds an endlessly long string now gets the error the reference browser gives instead of exhausting memory, and a new set of checks runs every test in every engine tier, which found and fixed several answers that differed from the reference.
Helpers inside helpers run compiled
Modern code splits work into small functions that call each other: a loop calls h(x), which calls sq(x). Such a loop used to drop out of compiled code entirely because a compiled body could not itself make a call. Now a small helper that calls another small helper is copied into the loop whole, and so are functions with default parameters (b = 2) and rest parameters (...rest), and objects a loop builds with new and only reads are kept in registers instead of being allocated. Each copied helper is re-checked every time the loop starts, so a script that swaps one out still gets its new version.
Array.from in one pass
Array.from({length: n}, (_, i) => i) is the everyday way to build a range. It read every index with a lookup up the prototype chain and called the callback through the slowest tier. It now reads a plain object in one pass and runs the callback as compiled code, and it also does what the standard says for objects with getters, prototype indexes and proxies, which it used to skip.
Long strings hold a fraction of the memory
A string built by += kept a small record for every piece appended, about 234 bytes for each 8-byte piece, so ten million appends held 2.5 GB for an 80 MB string. The newest piece of a string now has room to grow in place: appends write into it and nothing any other value reads is ever overwritten. Closures and objects also hold less: a call reserves exactly the slots its function binds, and a scope a closure keeps alive gives its spare room back.
Events on busy pages
Every event listener on a page lived in one list, and adding, removing or dispatching an event searched all of it at every element the event passed. A page with thousands of listeners paid for all of them on every click. Listeners are now indexed by element, event type and callback, so each step costs only the listeners it can run. A listener that an earlier listener removes during the same event no longer runs, as the standard says.
Every test in every tier
The engine runs a script in up to five tiers, from the interpreter to native machine code, and a bug can hide in one of them. A new tier matrix runs every edge-case test in eight tier configurations and with every safety switch on its own, and a fuzzer writes random programs and compares the tiers against each other. They found that compiled loops kept the built-in push after a script replaced it, that a key added to Object.prototype was invisible to in and for-in, and that strings had no length limit, so a doubling loop could take the whole machine's memory. All are fixed, and both checks now run on every change.
What changed 14 changes
Try it in your browser
Each check runs live in the browser you are reading with. Nothing is sent anywhere.