--- title: "New JavaScript features I didn’t know about" status: published date: 2025-06-05T14:52:18+0100 --- Now that going bouldering once a week no longer counts as having a personality, maybe I should try to stand out by being that guy who says "ECMAScript" instead of "JavaScript". Anyway, here are some new(-ish) JavaScript features that I recently discovered. ## `groupBy` (2024) There are now built-in functions to group array elements. With [`Object.groupBy(items)`][object-group] the result will be a normal object, and with [`Map.groupBy(items)`][map-group] the result will be a `Map`. Both of these have been widely available since March 2024, so they're ready to use. ## Iterator helpers (2025) I've always been irrationally upset that JavaScript's iterators aren't as ergonomic as Rust's. In Rust, you write something like ```rust things.iter() .map(|thing| do_stuff(&thing)) .filter(|thing| do_stuff2(&thing)) .map(|thing| do_stuff3(&thing)) .collect(); ``` and that turns your original `things` into a lazy iterator, then does a bunch of operations, and then `collect()` turns it back into a `Vec`. The iterators just go through the items one by one instead of allocating a whole pointless intermediate array. If you do the same thing in JavaScript, it looks like ```js things .map((thing) => do_stuff(thing)) .filter((thing) => do_stuff2(thing)) .map((thing) => do_stuff3(thing)); ``` Semantically that does the same thing as the Rust code, but each `map` and `filter` allocates an intermediate array. If the initial `things` is large, this could theoretically be a performance issue. So far, the way to optimise the above code would be to just use `for` loops, but `for` loops are cringe, so the fine folks at TC39 have blessed us with the same helper methods found on `Array`, but for `Iterator`s. So you can write ```js Iterator.from(things) .map((thing) => do_stuff(thing)) .filter((thing) => do_stuff2(thing)) .map((thing) => do_stuff3(thing)) .toArray(); ``` and bask in the glory of some beautiful premature optimisation. These methods are available in all modern browsers as of 2025, so are also ready to use. ## Error.isError (2025/2026?) If you ever get annoyed at `catch (error: unknown)` in TypeScript, there is now a reliable way of checking whether something is an error, and that is `Error.isError(error)`. It's more readable than ```js error && typeof error === "object" && "message" in error && typeof error.message === "string"; ``` and it's more reliable than `error instanceof Error`, because the `instanceof` check apparently fails when the error was constructed in a different [realm][realms]. `Error.isError` is available in all modern browsers, except that Safari will currently return `false` for `DOMException` objects, which it shouldn't. So this one is _almost_ ready to use. ## `TypedArray.prototype.subarray` This one's actually been around for 10 years, but I only discovered it recently (partly because I've never really used `TypedArray`s before). Unlike `TypedArray.prototype.slice`, [`TypedArray.prototype.subarray`][subarray] creates a new `TypedArray` that points to the same block of memory used by the original, and the data isn't copied. I found this useful when I was writing (part of) a disassembler, which looked kind of like this: ```ts function decode_instruction(data: Uint8Array): [Instruction, Uint8Array] { // Some horrifying ungodly logic return [ my_instruction, data.subarray(however_many_bytes_my_instruction_has_taken_up), ]; } function* decode_instructions(data: Uint8Array): Generator { let rest = data; while (rest.length > 0) { const [instruction, new_rest] = decode_instruction(rest); yield instruction; rest = new_rest; } } ``` With `TypedArray.prototype.slice`, this would have allocated a whole new copy of the input data after each instruction was decoded. Without `subarray`, I'd have had to just keep a reference to the original array and pass around an offset index, and that would have been so inconvenient. [realms]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Execution_model [subarray]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/subarray [map-group]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/groupBy [object-group]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/groupBy