web-dev-qa-db-fra.com

Pourquoi JavaScript ES6 ne prend-il pas en charge les classes multi-constructeurs?

Je veux écrire ma classe Javascript comme ci-dessous.

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

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

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

Je pense que ce serait bien si nous pouvions écrire un cours de cette façon. Attendez-vous à arriver:

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}
44
Manhhailua

Je veux écrire ma classe Javascript comme ci-dessous

Vous ne pouvez pas, de la même manière, ne pas surcharger les fonctions standard de ce type. Ce que vous pouvez faites, c'est utiliser l'objet arguments pour interroger le nombre d'arguments transmis:

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 Exemple

Bien sûr (avec votre exemple mis à jour), vous pouvez considérer que le nombre d’arguments ne vous intéresse pas, mais que chaque valeur individuelle est passé, auquel cas vous pourriez avoir quelque chose comme:

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;
    }
}
29
CodingIntrigue

Ce que vous voulez s'appelle la surcharge du constructeur. Ceci, et le cas plus général de surcharge de fonction , n'est pas pris en charge dans ECMAScript.

ECMAScript ne gère pas les arguments manquants de la même manière que les langages plus stricts. La valeur des arguments manquants est laissée sous la forme undefined au lieu de générer une erreur. Dans ce paradigme, il est difficile/impossible de détecter la fonction surchargée que vous visez.

La solution idiomatique consiste à avoir une seule fonction et à la faire gérer toutes les combinaisons d'arguments dont vous avez besoin. Pour l'exemple original, vous pouvez simplement tester la présence de key et value comme ceci:

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

Une autre option serait d’autoriser votre constructeur à prendre un objet lié à vos propriétés de classe:

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}

Ceci est encore plus utile avec TypeScript, car vous pouvez assurer la sécurité du type avec les valeurs transmises (c'est-à-dire que key ne peut être qu'une chaîne, autoLoad un booléen, etc.).

6
GFoley83

Voici un hack pour la surcharge basée sur l'arity (nombre d'arguments). L’idée est de créer une fonction à partir de plusieurs fonctions d’arités différentes (déterminée en regardant 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);

Bien sûr, cela nécessite beaucoup de protection contre les balles et de nettoyage, mais vous avez l’idée. Cependant, je ne pense pas que vous aurez beaucoup de chance de l’appliquer aux constructeurs de la classe ES6, car c’est un cheval de couleur différente.

6
user663031

D'après votre exemple de code, il vous suffit d'utiliser les valeurs par défaut pour vos paramètres:

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

Cela dit, une autre alternative à la surcharge de constructeur consiste à utiliser des usines statiques. Supposons que vous souhaitiez pouvoir instancier un objet à partir de paramètres simples, d'un hachage contenant ces mêmes paramètres ou même d'une chaîne JSON:

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}');
3
Diogo Sperb

vous pouvez utiliser des méthodes statiques, regardez ma réponse à la même 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)
1
mahdi shahbazi