'Dynamically extending a class in javascript

How can we dynamically/programmatically extend a javascript class?

More concretely, given something like

class Polygon {
  constructor(area, sides) {
    this.area = area;
    this.sides = sides;
  }
}

const Rectangle = extend(Polygon, (length, width) => {
  super(length * width, 4);
  this.length = length;
  this.width = width;
});

how can we implement something like extend such that it behaves the same as

class Rectangle extends Polygon {
  constructor(length, width) {
    super(length * width, 4);
    this.length = length;
    this.width = width;
  }
}

?



Solution 1:[1]

There are three problems here:

(1) super is only available inside object methods, so there is no way to access super in an arrow function. That needs to be somehow replaced by a regular function call.

(2) Classes can only be constructed, not called (unlike functions acting as constructors). Therefore you cannot just .call the classes constructor onto the "subclass" instance. You have to create an instance of the superclass and copy that into the subclass, eventually loosing getters / setters.

(3) Arrow functions have a lexical this, so you cannot access the instance with this inside an arrow function.

Given these three problems, a viable alternative would be:

  function extend(superclass, constructor) {
    function Extended(...args) {
      const _super = (...args) => Object.assign(this, new superclass(...args));
      constructor.call(this, _super, ...args);
    }
    Object.setPrototypeOf(Extended, superclass);
    Object.setPrototypeOf(Extended.prototype, superclass.prototype);
    return Extended;
 }

  const Rectangle = extend(Polygon, function(_super, length, width) {
     _super(/*...*/);
     /*...*/
  });

But honestly ... what's wrong with the native class ... extends ?

Solution 2:[2]

After some hacking around, I've found that this horrifyingly works.

function extend(superclass, construct) {
    return class extends superclass {
        constructor(...args) {
            let _super = (...args2) => {
                super(...args2)
                return this;
            };
            construct(_super, ...args);
        }
    };
}

const Rectangle = extend(Polygon, function(_super, length, width) {
         let _this = _super(length * width, 4);
         _this.length = length;
         _this.width = width;
    });

Solution 3:[3]

class A {
  m () { 
    console.log('A')
  }
}

class B extends A {
  m () { 
    console.log('B')
  }
}

var a = new A()
var b = new B()
a.m()
b.m()

const MixinClass = superclass =>
  class extends superclass {
    m () {
      console.log('extended')
    }
 }

const extendsAnyClass = AnyClass => 
    class MyMixinClass extends MixinClass(AnyClass) {}

var AA = extendsAnyClass(A)
var BB = extendsAnyClass(B)
var aa = new AA()
var bb = new BB()
aa.m()
bb.m()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 kag0
Solution 2 kag0
Solution 3