Search code examples
javascriptarraysconstructorderived-classes6-class

Why in javascript filtering an object of a class that extends an Array calls it's constructor?


I have this example code:

class TestClass extends Array {
	constructor() {
		console.log( 'constructor' );
		
		let ar = [];
		ar.push( { id: 1, name: 'a' } );
		ar.push( { id: 2, name: 'b' } );
		ar.push( { id: 3, name: 'c' } );
		ar.push( { id: 4, name: 'd' } );
		
		// finalizing object
		super( ...ar );
	}
	
	Foo() {
		console.log( 'foo' );
		return this.filter( item => item.id > 2 );
	}
}

let t = new TestClass();
console.log( t.Foo() );

It is simpler version of what I've already written. My app worked up till now, but stopped the moment I needed to filter data in my extended array. I've found out, that the problem is that calling a filter function on an object of my class internally calls constructor. The above code shows that example. Is there any way to bypass this issue, because I can't call constructor again at this point. Also, I've found out (using this simple TestClass) that actual output is not what I would expect - I get an array of 4 items with id's 3, 4, 3, 4. Can anyone explain what's happening here?


Solution

  • Symbol.species provides a way to return not another instance of the derived class but for .e.g. in this case an Array instance again.

    class TestClass extends Array {
      constructor() {
        console.log( 'constructor' );
    
        let ar = [];
        ar.push( { id: 1, name: 'a' } );
        ar.push( { id: 2, name: 'b' } );
        ar.push( { id: 3, name: 'c' } );
        ar.push( { id: 4, name: 'd' } );
    
        // finalizing object
        super( ...ar );
      }
      static get [Symbol.species]() { return Array; }
    
      Foo() {
        console.log( 'foo' );
        return this.filter( item => item.id > 2 );
      }
    }
    
    let t = new TestClass();
    let a = t.Foo();
    
    console.log('a : ', a);
    console.log('(a instanceof Array) ? ', (a instanceof Array));
    console.log('(a instanceof TestClass) ? ', (a instanceof TestClass));
    .as-console-wrapper { max-height: 100%!important; top: 0; }