Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call function from the constructor of a JS class

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?

like image 987
user1272965 Avatar asked Apr 18 '17 17:04

user1272965


People also ask

Can you call a function in the constructor JavaScript?

Yes, it is possible, when your constructor function executes, the this value has already the [[Prototype]] internal property pointing to the ValidateFields.

Can we call class function in constructor?

Initializing Objects in ConstructorYou can call other class methods from the constructor because the object is already initialized. The constructor also creates an object whose properties have their default values — either empty ( [] ) or the default value specified in the property definition block.

How do you call a class method in constructor?

Invoking a constructor from a method No, you cannot call a constructor from a method. The only place from which you can invoke constructors using “this()” or, “super()” is the first line of another constructor. If you try to invoke constructors explicitly elsewhere, a compile time error will be generated.


1 Answers

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.

like image 187
Joe Frambach Avatar answered Oct 23 '22 04:10

Joe Frambach