1

This is my first question on Stackoverflow so please dont run over me like a bulldozer if i did something wrong :)

I need to know if its possible in JavaScript classes to know, if the child has provided a constructor.

E.g.

class Parent {
  constructor() {
    console.log('Child has constructor:', /* Magic here */)
  }
}

class Child extends Parent {}

new Child()

Expected output: Child has constructor: false

Vs:

class Parent {
  constructor() {
    console.log('Child has constructor:', /* Magic here */)
  }
}

class Child extends Parent {
  constructor() {
    super()
  }
}

new Child()

Expected output: Child has constructor: true

Background: I would like to have a class that behaves differently when it was extended than if it was used directly. Since Childs should provide the Parent different informations than if it was used directly.

Freefant
  • 13
  • 4

3 Answers3

2

You could add one parameter to the Parent constructor that is false by default and then when you call the super inside the Child class you pass true for this parameter.

class Parent {
  constructor(called = false) {
    console.log('Child has constructor:', called)
  }
}

class Child extends Parent {
  constructor() {
    super(true)
  }
}

class ChildTwo extends Parent {}

new Child()
new ChildTwo()
Nenad Vracar
  • 118,580
  • 15
  • 151
  • 176
  • But then i would not be able to use the Parent class independently for other usages right? For example if i expect the user to provide a object as Parameter. – Freefant Nov 11 '20 at 14:39
  • You can use parent independently, `const parent = new Parent()`, the only issue would be if you wanted to pass more parameters to the parent – Vinícius Negrão Nov 11 '20 at 14:45
  • Yea thats what i would like to do. If Parent was used directly, have different behaviour than if it was extended. – Freefant Nov 11 '20 at 14:47
  • if you want you can declare the Parent constructor as so `class Parent { constructor({ called = false, ...args } = {}){ console.log(called, args) }}` then you can use whichever arguments you want and also pass `({ called: true })` from children – Vinícius Negrão Nov 11 '20 at 14:49
  • 1
    Mhh interesting. The object approach could work. I will give it a try and give my feedback :) – Freefant Nov 11 '20 at 14:50
  • Okay i think i will do it this way i guess – Freefant Nov 11 '20 at 15:13
1

No, it's not possible.

The class syntax in JavaScript is just a syntax suger of a normal function.

Take this ES6 example:

class Parent {
  constructor(value){
    this.example = value;
  }
  parentMethod(){
    console.log('parent:', this.example);
  }
}

class Child extends Parent {
  childMethod(){
    console.log('children:', this.example);
  }
}

const parent = new Parent('Hello');
const child = new Child('World');

parent.parentMethod();
child.childMethod();

console.log(parent.constructor);
console.log(child.constructor);

As you can see, even if you don't explicitly define a constructor, a class will always have a constructor.

The above could roughly translate into the below ES5 code which does not yet support class syntax:

function Parent(value){
  this.example = value;
}

Object.defineProperties(Parent.prototype, {
  parentMethod: {
    writable: true,
    enumerable: false,
    configurable: true,
    value: function(){
      console.log('parent:', this.example);
    }
  }
});

function Child(value){
  Parent.call(this, value);
}

Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Child.prototype = Object.defineProperties(Child.prototype, {
  childMethod: {
    writable: true,
    enumerable: false,
    configurable: true,
    value: function(){
      console.log('child:', this.example);
    }
  }
});

var parent = new Parent('Hello');
var child = new Child('World');

parent.parentMethod();
child.childMethod();

console.log(parent.constructor);
console.log(child.constructor);

As you can see:

  1. A class is merely just a function.
  2. The .constructor is always assigned.

Hence, there is no way for you to check if child class has a constructor because constructor is always there.

Even if you can do it (which you can't), the Parent will not know beforehand what class will extend it, so it will not know whether or not a future child will have a constructor or not.

yqlim
  • 6,898
  • 3
  • 19
  • 43
  • Exactly what i feared. Thanks for explaining it :) Ps: Using .toString() would be ugly but still work, right? – Freefant Nov 11 '20 at 14:55
  • By using this in the Parent: `console.log(!!this.constructor.toString().match(/constructor\s*\(/))` – Freefant Nov 11 '20 at 15:07
  • 1
    @Freefant no it won't work because (1) the child could have something like `const constructor = 'hello'`, then you'll get a false positive, (2) when you transpile the code using babel (or something like that), the `.toString()` might give you a different value depending on the transpiler output. – yqlim Nov 11 '20 at 15:18
0

I need to know if its possible in JavaScript classes to know, if the child has provided a constructor.

There are no classes without a constructor. Some classes might have an implicit constructor (no constructor in the class syntax), but you cannot - and should not - detect that.

I would like to have a class that behaves differently when it was extended than if it was used directly

You can distinguish a new Child from a new Parent call using new.target == Parent.

Childs should provide the Parent different information than if it was used directly.

That's a bad idea. Your parent constructor should have an interface that doesn't care about who is using it. I would suggest to either

  • never use your parent class directly, but extend it yourself and provide that class for default usage. It can handle the different information.
  • make the parent class accept different kinds of information, i.e. overload its interface. Distinguish what information you were given, and behave accordingly. However, it should not matter whether the information was given directly by a user or produced by a child class.
Bergi
  • 630,263
  • 148
  • 957
  • 1,375