Manhhailua
Manhhailua

Reputation: 1082

Why doesn't JavaScript ES6 support multi-constructor classes?

I want to write my Javascript class like below.

class Option {
    constructor() {
        this.autoLoad = false;
    }

    constructor(key, value) {
        this[key] = value;
    }

    constructor(key, value, autoLoad) {
        this[key] = value;
        this.autoLoad = autoLoad || false;
    }
}

I think it would be nice if we can write out class in this way. Expect to happen:

var option1 = new Option(); // option1 = {autoLoad: false}
var option2 = new Option('foo', 'bar',); // option2 = {foo: 'bar'}
var option3 = new Option('foo', 'bar', false); // option3 = {foo: 'bar', autoLoad: false}

Upvotes: 57

Views: 47188

Answers (8)

Bogatitus
Bogatitus

Reputation: 21

Its not the overload I wanted, but this is a basic version of how I faked my way through creating an obj1 with some different initialization behavior. I realize I could have expanded the arguments as stated above, but I already had a nasty set of arguments and relatively different data sources to deconstruct that would have really distorted my objectives; this just made it cleaner for my situation...

class obj1{
  constructor(v1, v2){
    this.a = v1;
    this.b = v2;
  }
}

class obj1Alt{
  constructor(v1, v2){
    return new obj1(v1*2,v2*2);
  }
}

new obj1(2,4) // returns an obj1
new obj1Alt(2,4) // also returns an obj1

Disclaimer: I've been programming for a long time, but I am fairly new to JS; probably not a best practice.

Upvotes: 0

Azat
Azat

Reputation: 1

Use object.assigne with arguments with this

This={...this,...arguments}

Upvotes: 0

mahdi shahbazi
mahdi shahbazi

Reputation: 2132

you can use static methods,look at my answer to same question

class MyClass {
    constructor(a,b,c,d){
        this.a = a
        this.b = b
        this.c = c
        this.d = d
    }
    static BAndCInstance(b,c){
        return new MyClass(null,b,c)
    }
}

//a Instance that has b and c params
MyClass.BAndCInstance(b,c)

Upvotes: 2

Diogo Eichert
Diogo Eichert

Reputation: 499

Guessing from your sample code, all you need is to use default values for your parameters:

class Option {
    constructor(key = 'foo', value = 'bar', autoLoad = false) {
        this[key] = value;
        this.autoLoad = autoLoad;
    }
}

Having said that, another alternative to constructor overloading is to use static factories. Suppose you would like to be able to instantiate an object from plain parameters, from a hash containing those same parameters or even from a JSON string:

class Thing {
    constructor(a, b) {
        this.a = a;
        this.b = b;
    }

    static fromHash(hash) {
        return new this(hash.a, hash.b);
    }

    static fromJson(string) {
        return this.fromHash(JSON.parse(string));
    }
}

let thing = new Thing(1, 2);
// ...
thing = Thing.fromHash({a: 1, b: 2});
// ...
thing = Thing.fromJson('{"a": 1, "b": 2}');

Upvotes: 12

GFoley83
GFoley83

Reputation: 3539

Another option would be to allow your constructor to take an object that is bound to your class properties:

class Option {
  // Assign default values in the constructor object 
  constructor({key = 'foo', value, autoLoad = true} = {}) {
      this.key = key;
      // Or on the property with default (not recommended)
      this.value = value || 'bar';
      this.autoLoad = autoLoad;
      
      console.log('Result:', this);
  }
}

var option1 = new Option();
// Logs: {key: "foo", value: "bar", autoLoad: true}

var option2 = new Option({value: 'hello'});
// Logs: {key: "foo", value: "hello", autoLoad: true}

This is even more useful with Typescript as you can ensure type safety with the values passed in (i.e. key could only be a string, autoLoad a boolean etc).

Upvotes: 10

Bardi Harborow
Bardi Harborow

Reputation: 1888

What you want is called constructor overloading. This, and the more general case of function overloading, is not supported in ECMAScript.

ECMAScript does not handle missing arguments in the same way as more strict languages. The value of missing arguments is left as undefined instead of raising a error. In this paradigm, it is difficult/impossible to detect which overloaded function you are aiming for.

The idiomatic solution is to have one function and have it handle all the combinations of arguments that you need. For the original example, you can just test for the presence of key and value like this:

class Option {
  constructor(key, value, autoLoad = false) {
    if (typeof key !== 'undefined') {
      this[key] = value;
    }
    this.autoLoad = autoLoad;
  }
}

Upvotes: 22

user663031
user663031

Reputation:

Here's a hack for overloading based on arity (number of arguments). The idea is to create a function from a number of functions with different arities (determined by looking at fn.length).

function overloaded(...inputs) {
  var fns = [];

  inputs.forEach(f => fns[f.length] = f);

  return function() {
    return fns[arguments.length].apply(this, arguments);
  };
}

var F = overloaded(
  function(a)    { console.log("function with one argument"); },
  function(a, b) { console.log("function with two arguments"); }
);

F(1);
F(2, 3);

Of course this needs a lot of bullet-proofing and cleaning up, but you get the idea. However, I don't think you'll have much luck applying this to ES6 class constructors, because they are a horse of a different color.

Upvotes: 6

CodingIntrigue
CodingIntrigue

Reputation: 78525

I want to write my Javascript class like below

You can't, in the same way you can't overload standard functions like that. What you can do is use the arguments object to query the number of arguments passed:

class Option {
    constructor(key, value, autoLoad) {
        // new Option()
        if(!arguments.length) {
            this.autoLoad = false;
        }
        // new Option(a, [b, [c]])
        else {
            this[key] = value;
            this.autoLoad = autoLoad || false;
        }
    }
}

Babel REPL Example

Of course (with your updated example), you could take the approach that you don't care about the number of arguments, rather whether each individual value was passed, in which case you could so something like:

class Option {
    constructor(key, value, autoLoad) {
        if(!key) { // Could change this to a strict undefined check
            this.autoLoad = false;
            return;
        }
        this[key] = value;
        this.autoLoad = autoLoad || false;
    }
}

Upvotes: 32

Related Questions