Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ES6 constructor returns instance of base class?

The constructor of a derived class returns an instance of the base class.

The following code explains my problem:

// Vector is defined by an external module (Unreal.js)
class TestB extends Vector {
    constructor() {
        super();
    }
    Log() {
        console.log("" + this);
    }
}
console.log(new TestB() instanceof TestB) // returns false !!! why ??? 
console.log(new TestB() instanceof Vector) // returns true...

class TestA extends Array {
    constructor() {
        super();
    }
    Log() {
        console.log("" + this);
    }
}
console.log(new TestA() instanceof TestA); // returns true, all is good

How is this possible?

like image 202
arthur.sw Avatar asked May 19 '16 17:05

arthur.sw


1 Answers

It would appear that Vector is implemented in a way that makes it incompatible with class.

Here's an example of one way Vector could do that:

function Vector() {
  var v = Object.create(Vector.prototype);
  return v;
}

class TestB extends Vector {
  constructor() {
    super();
  }
}

console.log(new TestB() instanceof TestB);  // false
console.log(new TestB() instanceof Vector); // true

The key here is that since Vector returns a different object than the one new created, it's of the wrong type. A relatively little-known thing about constructor functions is that if they return a non-null object reference, the result of new Constructor is the object the constructor returned, rather than the object new created.

Here's a snippet for those whose browsers support class:

function Vector() {
  var v = Object.create(Vector.prototype);
  return v;
}

class TestB extends Vector {
  constructor() {
    super();
  }
}

console.log(new TestB() instanceof TestB); // false
console.log(new TestB() instanceof Vector); // true

...and a live copy on Babel's REPL for those whose browsers don't.

To my surprise, both Babel and Chrome let me do this using class Vector as well and returning a value from constructor; I haven't figured out (yet) from the specification whether it's actually valid:

class Vector {
  constructor() {
    var v = Object.create(Vector.prototype);
    return v;
  }
}

class TestB extends Vector {
  constructor() {
    super();
  }
}

console.log(new TestB() instanceof TestB); // false
console.log(new TestB() instanceof Vector); // true

To get around it, you'll likely need to use a per-instance hack, like copying all of TestB.prototype's methods onto the instance. Ideally, rather than hacking, try to use Vector via aggregation (aka "composition", e.g., by having a Vector instance as a property of your class's instances) rather than inheritance, since it's not set up for inheritance.

like image 53
T.J. Crowder Avatar answered Oct 24 '22 01:10

T.J. Crowder