4

For a few years now I've been building and using a custom data analysis tool in JavaScript (repo, docs).

One of the changes I've made fairly recently is to use a JavaScript class to extend the native Array type, so I can easily call my custom functions as methods on an array-like object instead of passing them into those functions as an argument without losing the ability to access elements using [] notation.

To do this, I've been using the following code:

class AnalyserRows extends Array {
    constructor(sourceArray) {
        super(...sourceArray);
    }

    // ...
}

This has worked fine for a while, but recently I tried to load a particularly large set of data and I ran into a problem. I was trying to create an AnalyserRows object with about 66,000 elements, but Chrome throws this error:

Uncaught RangeError: Maximum call stack size exceeded

In my searching, I've found this explanation of the problem: RangeError - Maximum Call Stack Size Exceeded Error Using Spread Operator in Node.js / Javascript

The spread operator (and Array.assign) loads the entire source array onto the stack, then pushes it onto the destination array. Once the source array exceeds 125,052, it causes a Stack Overflow / Range Error.

So now my problem is how can I call super inside my custom class's constructor and pass through each element of the source array without using the spread operator?

It's possible to create an Array with this many elements without any real problem, so I feel like it should be possible to do the same with a class extending Array like this. But without using the spread operator or making some other change that will prevent me from using some of the syntax I've been using, I can't see how to do it.

5
  • How exactly were you calling AnalyserRows? Btw I doubt this subclass works at all. Commented Sep 11, 2020 at 0:17
  • Where "rows" is an existing Array, I was calling it like this: dataConfig.rows = new AnalyserRows(rows); It definitely works. I've been using it for ages before I ran into this issue. Just checked now and I can call regular Array methods like map and reduce no problem. Commented Sep 11, 2020 at 0:53
  • Maybe you also defined static [Symbol.species] = Array? Commented Sep 11, 2020 at 8:23
  • Nope. I understand that's only necessary if you want Array's native methods that normally return a new Array, like map and reduce, to still return an Array. I don't want that. I linked to the repo in my question if you're interested to take a look at the code. The file in question is analyser.js Commented Sep 12, 2020 at 23:45
  • With the spread syntax call, new AnalyserRows([]).map(x => x) does throw a TypeError: Found non-callable @@iterator for me - just like in the linked question. With super(sourceArray.length), it will return AnalyserRows [undefined] (of length 1) because you're passing undefined to the super call. Not sure why you wouldn't be able to reproduce that? Commented Sep 12, 2020 at 23:57

1 Answer 1

4

The Array constructor has two forms: you can pass it all the array elements, or you can pass it the size of the array. You could try using the second form, then copying the array elements in your constructor.

class AnalyserRows extends Array {
    constructor(sourceArray) {
        super(sourceArray.length);
        for (let i = 0; i < sourceArray.length; i++) {
            this[i] = sourceArray[i];
        }
    }

    // ...
}

However, this may be very slow for huge arrays. From the quote you gave, it sounds like any of the built-in shortcuts will run into the stack overflow problem.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks Barmar, I've just tried that and it works. Quite a simple solution, now that you've suggested it I feel silly for not thinking of it myself! It looks like it takes just 2.4 ms to create the 66,000 element array with this method, at least with this example data, so I don't feel too worried about it being too slow at the moment.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.