How to call function from the constructor of a JS class

15,814

Removing extends Object and the super() call seems to work for me.

class Matrix { // extends Object {
    constructor(r, c) {
        // super();
        this.assign();
    }
    assign() {
    }
}

Every class has Object in its prototype chain already, so you don't gain anything by extending Object. extends Object is implicit, so remove it.


It seems that the handling of extends Object is a bug in Babel. The Chromium browser's built-in es6 engine handles the code just fine, since it was fixed here: https://bugs.chromium.org/p/v8/issues/detail?id=3886

But it looks like Babel's transpiled es5 is broken.

Working es6 in Chrome


To somewhat explain this behavior, let's take a look at the transpiled es5 from babel.

Given this es6:

class OtherClass {}

class OtherClassExtender extends OtherClass {
    constructor(r, c) {
        super();
        this.assign();
    }
    assign() {
    }
}

class ObjectExtender extends Object {
    constructor(r, c) {
        super();
        this.assign();
    }
    assign() {
    }
}

new OtherClassExtender(1, 2);
new ObjectExtender(1, 2);

We'll take just a snippet of the transpiled es5. I've removed a lot of code. This is not the full transpiled source:

function _possibleConstructorReturn(self, call) {
  if (!self) {
    throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  }
  if (call && (typeof call === "object" || typeof call === "function")) {
    return call;
  } else {
    return self;
  }
}

var OtherClass = function OtherClass() {};

var OtherClassExtender = function (_OtherClass) {    
    function OtherClassExtender(r, c) {
        var _this = _possibleConstructorReturn(this, _OtherClass.call(this));
        _this.assign();
        return _this;
    }
    OtherClassExtender.prototype.assign = function assign() {};
    return OtherClassExtender;
}(OtherClass);

var ObjectExtender = function (_Object) {
    function ObjectExtender(r, c) {
        var _this2 = _possibleConstructorReturn(this, _Object.call(this));
        _this2.assign();
        return _this2;
    }
    ObjectExtender.prototype.assign = function assign() {};
    return ObjectExtender;
}(Object);

new OtherClassExtender(1, 2);
new ObjectExtender(1, 2);

_possibleConstructorReturn is basically super, where Babel is keeping a reference of the class' super-class so method calls can properly traverse the inheritance hierarchy. It basically says "The super-class has methods defined, so let's look there for methods (like assign)". There's no sense in adding Object itself to that hierarchy since any object in Javascript will inherit methods from Object anyway.

By stepping through the transpiled code, _possibleConstructorReturn(OtherClassExtender, OtherClass) says typeof call is undefined so it returns OtherClassExtender.

_possibleConstructorReturn(ObjectExtender, Object) says typeof call is object so it returns Object.

Object instances don't have an assign method. assign is a class method on the Object class, it doesn't exist on (new Object).assign for example.

Share:
15,814
user1272965
Author by

user1272965

Updated on June 30, 2022

Comments

  • user1272965
    user1272965 almost 2 years

    I'm creating a matrix class (to be used in a react app) which I'd like define this way:

    class Matrix extends Object {
        constructor(r, c) {
            super();
            this.rows = new Array(r);
            for (var i=0; i<r; i++) {
                this.rows[i] = new Array(c);
            }
            this.assign((v,i,j) => (i===j)? 1:0);   // ERROR HERE
        }
        assign(f) {
            for (var i=0; i<this.rows.length; i++) {
                for (var j=0; j<this.rows[i].length; j++) {
                    this.rows[i][j] = f(this.rows[i][j], i, j);
                }
            }
        }
        // and so on
    

    This compiles okay via webpack, but while running, I get an error in the chrome console saying that _this.assign is not a function.

    I can make it work this way:

    constructor(r, c) {
        super();
        this.rows = new Array(r);
        for (var i=0; i<r; i++) {
            this.rows[i] = new Array(c);
        }
        this.assign = function(f) {
            for (var i=0; i<r; i++) {
                for (var j=0; j<r; j++) {
                    this.rows[i][j] = f(this.rows[i][j], i, j);
                }
            }
        };
        this.assign((v,i,j) => (i===j)? 1:0);
    }
    

    But that's wrong, right? I shouldn't have to define all of an object's functions in the constructor, right? In the very same file, I define react classes like:

    class MatrixComponent extends React.Component { ... }
    

    and those are able to call functions without defining them in the constructor. What am I missing?