Live data from Hacker News

Some features that every JavaScript developer should know in 2025

waspdev.com

11–17 of 17 posts

Re: Some features that every JavaScript developer should know in 2025

#11
post #10

Earlier quoted context omitted.

Sorry, but your comment is completely, completely wrong. > .slice[0] does not allocate, nor does .filter[1], only map does.. so one allocation. This is simply not true. I presume you’re misunderstanding “shallow copy” in the MDN docs; it’s pretty poor wording, in my opinion: it means shallow copies of the items , it’s not about the array; it’s not like a live collection, they all do create a new Array. Array.prototyp…

You're right.. all of these functions require more memory. Allocate is wrong... let's use shallow vs deep (significantly different expense). All pointers use a bit more memory than the original, shallow copies use more, but only the size of a primitive (best case) or pointer to an object (worst case.. often the same size), deep can use much, much more. > arr.slice(10, 20).filter(el => el el + 5) slice = shallow filte…

You’re counting completely the wrong thing. Shallow versus deep is about the items inside, but we care about the costs of creating the collection itself. As far as structured clones are concerned, none of the operations we’re talking about are deep. At best, it’s just the wrong word to use. (Example: if you were going to call it anything, you’d call .map(x => x) shallow.)

Array:

• Array.prototype.slice may be expensive (it creates a collection, but it may be able to be done in such a way that you can’t tell).

• Array.prototype.filter is expensive (it creates a collection).

• Array.prototype.map is expensive (it creates a collection).

So you have two or three expensive operations, going through as much as the entire list (depends on how much you trim out with slice and filter) two or three times, creating an intermediate list at each step.

Iterator:

• Array.prototype.values is cheap, creating a lightweight iterator object.

• Iterator.prototype.drop is cheap, creating a lightweight iterator helper object.

• Iterator.prototype.take is cheap, creating a lightweight iterator helper object.

• Iterator.prototype.filter is cheap, creating a lightweight iterator helper object.

• Iterator.prototype.map is cheap, creating a lightweight iterator helper object.

• Iterator.prototype.toArray is the thing that actually drives everything. Now you drive the iterator chain through, going through the list only once, applying each filter or transformation as you go, and only doing one expensive allocation of a new array.

In the end, in terms of time complexity, both are O(n), but the array version has a much higher coefficient on that n. For small inputs, array may be faster. For large values, iterators will be faster.

Re: Some features that every JavaScript developer should know in 2025

#12
post #3
post #2

Iterator Helpers: arr.slice(10, 20).filter(el => el el + 5) > This is really inefficient because for each transformation a new array should be allocated. .slice[0] does not allocate, nor does .filter[1], only map does.. so one allocation. arr.values().drop(10).take(10).filter(el => el el + 5).toArray() Allocates once for .values[2], and again for .toArray[3].. there's decreased efficiency here. Swapping variables : O…

Vast majority of JS/TS we write doesn’t live on the critical path and should be optimised for readability over performance. However, when it does, it should be explicitly recognised, appropriate boundaries put, and that code written in a completely different style altogether. There are A LOT more of idiomatic and widespread JS/TS patterns that should not be used in such code. Before TS, I used to write games in C# (U…

To be fair Unity is somewhat special because it requires a lot more massaging to get acceptable performance in the happy path than standard C# code. In the latter you mainly care about large allocations if application is latency-tolerant (i.e. as long as you don't touch LOH it's fine) or avoiding allocations in general if it's latency-sensitive. There isn’t that much difference with regular sane C# code.

Re: Some features that every JavaScript developer should know in 2025

#13
post #4

I wish authors would actually test their performance claims before publishing them. I quickly benchmarked the two code snippets: arr.slice(10, 20).filter(el => el el + 5) and arr.values().drop(10).take(10).filter(el => el el + 5).toArray() but scaled up to much larger arrays. On V8, both allocated almost exactly the same amount of memory, but the latter snippet took 3-4x as long to run.

I'm am the author of this blog. As for speed, you are probably right, I was mainly talking about wasting memory for temporary arrays, not the speed, it's unlikely that iterators are faster. But I'm curious, how large arrays did you test with? For example, will there be a memory difference for 10M size arrays.

Re: Some features that every JavaScript developer should know in 2025

#14
post #2

Iterator Helpers: arr.slice(10, 20).filter(el => el el + 5) > This is really inefficient because for each transformation a new array should be allocated. .slice[0] does not allocate, nor does .filter[1], only map does.. so one allocation. arr.values().drop(10).take(10).filter(el => el el + 5).toArray() Allocates once for .values[2], and again for .toArray[3].. there's decreased efficiency here. Swapping variables : O…

Sorry, but your comment is completely, completely wrong. > .slice[0] does not allocate, nor does .filter[1], only map does.. so one allocation. This is simply not true. I presume you’re misunderstanding “shallow copy” in the MDN docs; it’s pretty poor wording, in my opinion: it means shallow copies of the items , it’s not about the array; it’s not like a live collection, they all do create a new Array. Array.prototyp…

> My own hypothesis: any serious JS engine is going to recognise the [a, b] = [b, a]

This. As the author of this blog I actually run a benchmark, the loop body was only doing swap, I remember the penalty was around ~2%. But yeah, if it's a critical path and you care about every millisecond, then sure, you should optimize for speed, not for code ergonomics.

Re: Some features that every JavaScript developer should know in 2025

#15
post #13
post #4

I wish authors would actually test their performance claims before publishing them. I quickly benchmarked the two code snippets: arr.slice(10, 20).filter(el => el el + 5) and arr.values().drop(10).take(10).filter(el => el el + 5).toArray() but scaled up to much larger arrays. On V8, both allocated almost exactly the same amount of memory, but the latter snippet took 3-4x as long to run.

I'm am the author of this blog. As for speed, you are probably right, I was mainly talking about wasting memory for temporary arrays, not the speed, it's unlikely that iterators are faster. But I'm curious, how large arrays did you test with? For example, will there be a memory difference for 10M size arrays.

> I was mainly talking about wasting memory for temporary arrays

Right, but the runtime is perfectly capable of optimizing those temporary arrays out, which it appears to do.

> I'm curious, how large arrays did you test with? For example, will there be a memory difference for 10M size arrays

10M size arrays are exactly what I tested with

Re: Some features that every JavaScript developer should know in 2025

#16
post #15
post #13

Earlier quoted context omitted.

I'm am the author of this blog. As for speed, you are probably right, I was mainly talking about wasting memory for temporary arrays, not the speed, it's unlikely that iterators are faster. But I'm curious, how large arrays did you test with? For example, will there be a memory difference for 10M size arrays.

> I was mainly talking about wasting memory for temporary arrays Right, but the runtime is perfectly capable of optimizing those temporary arrays out, which it appears to do. > I'm curious, how large arrays did you test with? For example, will there be a memory difference for 10M size arrays 10M size arrays are exactly what I tested with

Interesting, I did some testing, just opened the task manager and run this js code in the browser without opening dev tools in order to see how the browser will behave when I don't prevent any optimizations.

Then I commented withArrayTransform and uncommented withIteratorTransform and did again in a fresh tab to prevent the browser reusing the old process.

//////////////////////////////////////////////////////////////////////////////////

const arr = Array.from({length: 100000000}, (_, i) => i % 10);

function withArrayTransform(arr) { return arr.slice(10, arr.length - 10).filter(el => el el + 5).map(el => el * 2).map(el => el - 7); }

function withIteratorTransform(arr) { return arr.values().drop(10).take(arr.length - 20).filter(el => el el + 5).map(el => el * 2).map(el => el - 7).toArray(); }

console.log(withArrayTransform(arr)) //console.log(withIteratorTransform(arr))

////////////////////////////////////////////////////////////////////////////////////

The peak memory usage with withArrayTransform was about ~1.6GB. The peak memory usage with withIteratorTransform was about ~0.8GB. Results sometimes vary, and it honestly feels complicated, but the iterator version is consistently more memory efficient. As of the speed, the iterator version was about ~1.5 times slower.

So probably the GC quickly cleaned up some temporary arrays when it saw an excessive memory usage in the process of running withArrayTransform(arr).

But imagine you use flatMap which unrolls the returned iterable and it can create even a bigger temporary array than the original and the final one. So using iterables still has an advantage of protecting from excessive memory usage and potentially crashing the browser tab or the whole Node server. I think it's still a nice thing to have.

Re: Some features that every JavaScript developer should know in 2025

#17
post #15
post #13

Earlier quoted context omitted.

I'm am the author of this blog. As for speed, you are probably right, I was mainly talking about wasting memory for temporary arrays, not the speed, it's unlikely that iterators are faster. But I'm curious, how large arrays did you test with? For example, will there be a memory difference for 10M size arrays.

> I was mainly talking about wasting memory for temporary arrays Right, but the runtime is perfectly capable of optimizing those temporary arrays out, which it appears to do. > I'm curious, how large arrays did you test with? For example, will there be a memory difference for 10M size arrays 10M size arrays are exactly what I tested with

My speculation is also that with iterators the array size might be somewhat less predictable, because you might not know when the iterator finishes. For example by doing .filter().map(). So there is no way to precisely know how much memory will be preallocated.
Post reply on HN