Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

filter() is buggy, when I extend the Array class

I thought it would be useful to extend Arrays in javascript, to enhance them with some custom methods and business logic. It worked fine so far until I tried to filter on my instances.

It seems like the array filter method calls my constructor with one argument (0 if nothing is found) and returns a new instance. Not what I expected.

class MyArray extends Array {
  constructor(x, y, z) {
    super(x, y, z);
    // do custom stuff
  }
}

let numbers = new MyArray(1, 2, 3);
console.log(numbers.filter((v) => v === 0));

Though, if I pass and spread some arguments into a single variable, everything works.

Technically I see no difference between the two, I don't get it!

class MyArray extends Array {
  constructor(x, ...args) {
    super(x, ...args);;
    // do custom stuff
  }
}

let numbers = new MyArray(1, 2, 3);
console.log(numbers.filter((v) => v === 0));
  1. Why does the first approach run into this issue?
  2. Why does the second approach not run into this?

(Is it bad practice to extend Arrays or other core js classes?)

like image 408
Basti Avatar asked Oct 28 '25 08:10

Basti


1 Answers

The Array constructor behaves differently depending on the number and type of arguments it receives.

If it receives a single argument containing an integer, this argument is the initial size of the array. It returns an array that size, with the elements uninitialized.

In other cases, the arguments are the initial elements of the array.

So when you call super(x, y, z), you're using the second format, and it creates an array initially containing the values x, y, z.

filter() calls your constructor using the first format, so x is the size of the array it requested (0 as you said). But your constructor has additional arguments y and z, so those are being set to undefined. So you're calling super(0, undefined, undefined), which is creating an array [0, undefined, undefined].

Your second constructor is almost right, but the real Array constructor can be called with no arguments, returning an empty array. If your constructor is called that way, it will call super(undefined), creating an array with a single element containing undefined. The right way to define the constructor is with a single spread argument. This won't make a difference for filter(), but might affect other code.

class MyArray extends Array {
  constructor(...args) {
    super(...args);;
    // do custom stuff
  }
}

let numbers = new MyArray(1, 2, 3);
console.log(numbers.filter((v) => v === 0));
like image 68
Barmar Avatar answered Oct 29 '25 23:10

Barmar



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!