JavaScript-Muster für mehrere Konstruktoren


124

Ich benötige verschiedene Konstruktoren für meine Instanzen. Was ist ein gängiges Muster dafür?


Seien Sie bitte etwas genauer. Sie möchten Konstruktoren mit unterschiedlichen Parametersätzen?
Gblazex

Können Sie mehr als einen Konstruktor in Javascript haben?
Doug Hauf

Ja und nein @DougHauf. Ja, da die Antwort von Bobince eine Möglichkeit bietet, ein gleichwertiges Verhalten zu erzielen. Nein, denn wenn Sie mehrere unterschiedliche Konstruktorfunktionen wünschen (von denen jede dasselbe Prototypobjekt verwendet), wie würde die Konstruktoreigenschaft des Prototypobjekts festgelegt (da die Konstruktoreigenschaft nur auf eine Konstruktorfunktion verweisen kann).
Moika wird am

3
Alle diese Antworten sind alt / nicht ideal. Ich bin zu faul, um eine Antwort einzugeben, aber Sie können ein Objekt an Funktionen und Konstruktoren weitergeben und dann die Schlüssel wie Argumente verwenden, z function ({ oneThing = 7, otherThing = defaultValue } = {}) { }. B.: . Das Extra, das = {}ich dort eingefügt habe, ist ein weiterer Trick, den ich kürzlich gelernt habe, falls Sie die Möglichkeit haben möchten, dass der Benutzer überhaupt kein Objekt übergibt und alle Standardeinstellungen verwendet.
Andrew

1
Follow- up : Hier sind einige gute Möglichkeiten, um dieses Problem zu lösen: stackoverflow.com/a/32626901/1599699 stackoverflow.com/a/41051984/1599699 stackoverflow.com/a/48287734/1599699 Ich mag den letzten besonders gern Mehr Konstruktor artigen Träger, mit statischer Fabrik Funktionen wie Konstrukteure ( return new this();, return new this.otherStaticFactoryFunction();usw.)!
Andrew

Antworten:


120

JavaScript hat keine Funktionsüberladung, auch nicht für Methoden oder Konstruktoren.

Wenn sich eine Funktion je nach Anzahl und Art der Parameter, die Sie an sie übergeben, unterschiedlich verhalten soll, müssen Sie sie manuell abhören. JavaScript ruft gerne eine Funktion mit mehr oder weniger als der angegebenen Anzahl von Argumenten auf.

function foo(a, b) {
    if (b===undefined) // parameter was omitted in call
        b= 'some default value';

    if (typeof(a)==='string')
        this._constructInSomeWay(a, b);
    else if (a instanceof MyType)
        this._constructInSomeOtherWay(a, b);
}

Sie können auch argumentsals Array zugreifen , um weitere Argumente zu erhalten.

Wenn Sie komplexere Argumente benötigen, kann es eine gute Idee sein, einige oder alle in eine Objektsuche einzufügen:

function bar(argmap) {
    if ('optionalparam' in argmap)
        this._constructInSomeWay(argmap.param, argmap.optionalparam);
    ...
}

bar({param: 1, optionalparam: 2})

Python zeigt, wie Standard- und benannte Argumente verwendet werden können, um die meisten Anwendungsfälle praktischer und eleganter abzudecken als das Überladen von Funktionen. JavaScript, nicht so sehr.


2
Danke, das ist wirklich schön. Ich würde sagen, die zweite Option ist nicht nur nützlich, wenn Sie komplexe Argumente haben, sondern auch einfache, aber schwer zu unterscheidende Argumente, z . B. Unterstützung von MyObj({foo: "foo"})Plus MyObj({bar: "bar"}). MyObj hat zwei Konstruktoren - aber beide nehmen ein Argument, das eine Zeichenfolge ist :-)
Alex Dean

1
Können Sie dem Beispielcode für dieses spezielle Beispiel mehr hinzufügen?
Doug Hauf

Hallo @DougHauf, Crockfords Buch 'JavaScript: The Good Parts' enthält einen Abschnitt mit dem Namen 'Object Specifiers'. Viele Beispiele beziehen sich online darauf.
Moika wird am

29

Wie findest du das?

function Foobar(foobar) {
    this.foobar = foobar;
}

Foobar.prototype = {
    foobar: null
};

Foobar.fromComponents = function(foo, bar) {
    var foobar = foo + bar;
    return new this(foobar);
};

2
gib dies neu zurück (foobar); funktioniert nicht Ich wechsle bei der Rückkehr neue Foobar (Foobar); und alles ist Arbeit richtig.
Isxaker

10
Ich verstehe es nicht Können Sie den Code dort hinzufügen, wo Sie ihn tatsächlich verwenden? Müssen Sie jedes Mal fromComponents anrufen? Denn das ist nicht wirklich ein Konstruktor, sondern eine Hilfsfunktion. Die Antwort von @ bobince scheint dann genauer zu sein.
Hofnarwillie

1
@hofnarwillie Obwohl es sich nicht um einen exakten Konstruktor handelt, führt es mit einer statischen Methode eine ziemlich ähnliche Leistung aus, dh var foobarObj = Foobar.fromComponents (foo, bar); ist alles, was Sie brauchen, um ein neues Objekt mit den alternativen Argumenten zu erstellen.
Nathan Williams

19

Sie können eine Klasse mit statischen Methoden verwenden, die eine Instanz dieser Klasse zurückgeben

    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)
        }
        static BAndDInstance(b,d){
            return new MyClass(null,b, null,d)
        }
    }

    //new Instance just with a and other is nul this can
    //use for other params that are first in constructor
    const myclass=new MyClass(a)

    //an Instance that has b and c params
    const instanceWithBAndC = MyClass.BAndCInstance(b,c)

    //another example for b and d
    const instanceWithBAndD = MyClass.BAndDInstance(b,d)

Mit diesem Muster können Sie mehrere Konstruktoren erstellen


3
Dies ist die beste Antwort. Die anderen greifen auf das Parsen von Arrays zurück und erledigen eine Menge unnötiger Arbeit.
Blane Townsend

13

Ich hatte keine Lust, es von Hand zu machen, wie in Bobince's Antwort, also habe ich das Plugin-Optionsmuster von jQuery komplett abgerissen.

Hier ist der Konstruktor:

//default constructor for Preset 'class'
function Preset(params) {
    var properties = $.extend({
        //these are the defaults
        id: null,
        name: null,
        inItems: [],
        outItems: [],
    }, params);

    console.log('Preset instantiated');
    this.id = properties.id;
    this.name = properties.name;
    this.inItems = properties.inItems;
    this.outItems = properties.outItems;
}

Hier sind verschiedene Arten der Instanziierung:

presetNoParams = new Preset(); 
presetEmptyParams = new Preset({});
presetSomeParams = new Preset({id: 666, inItems:['item_1', 'item_2']});
presetAllParams = new Preset({id: 666, name: 'SOpreset', inItems: ['item_1', 'item_2'], outItems: ['item_3', 'item_4']});

Und hier ist, was das gemacht hat:

presetNoParams
Preset {id: null, name: null, inItems: Array[0], outItems: Array[0]}

presetEmptyParams
Preset {id: null, name: null, inItems: Array[0], outItems: Array[0]}

presetSomeParams
Preset {id: 666, name: null, inItems: Array[2], outItems: Array[0]}

presetAllParams
Preset {id: 666, name: "SOpreset", inItems: Array[2], outItems: Array[2]}

Ich habe das gleiche Muster jetzt auch in node.js gerollt mit: npmjs.com/package/extend
Jacob McKay

10

Wenn Sie die Antwort von eruciform weiter verfolgen, können Sie Ihren newAnruf in Ihre initMethode einbinden .

function Foo () {
    this.bar = 'baz';
}

Foo.prototype.init_1 = function (bar) {
    this.bar = bar;
    return this;
};

Foo.prototype.init_2 = function (baz) {
    this.bar = 'something to do with '+baz;
    return this;
};

var a = new Foo().init_1('constructor 1');
var b = new Foo().init_2('constructor 2');

Im Grunde genommen nehmen Sie hier das Objekt Foo und rufen dann die Parameter init_1 und init_2 mit den Prototypfunktionen auf. Sollten Ihre init_1 und init_2 die Wortfunktion dabei haben.
Doug Hauf

Muss es nach dem} im ersten Foo () ein Semikolon geben?
Doug Hauf

Danke Doug, ich habe die Änderung vorgenommen.
laughingbovine

Bist du sicher, dass das funktioniert? Ich konnte nicht verketten new Foo()und den Aufruf initzusammenfassen, da ich nicht auf Eigenschaften der Objekte zugreifen konnte. Ich musste rennenvar a = new Foo(); a.init_1('constructor 1');
Millie Smith

@MillieSmith Ich gebe zu, dass ich JS schon eine Weile nicht mehr geschrieben habe ... aber ich habe diesen Code einfach in die Chrome JS-Konsole eingefügt und die Kette von neu bis init hat funktioniert.
laughingbovine

3

Manchmal reichen Standardwerte für Parameter für mehrere Konstruktoren aus. Und wenn das nicht ausreicht, versuche ich, den größten Teil der Konstruktorfunktionalität in eine init-Funktion (other-params) zu packen, die anschließend aufgerufen wird. Verwenden Sie auch das Factory-Konzept, um ein Objekt zu erstellen, mit dem die anderen gewünschten Objekte effektiv erstellt werden können.

http://en.wikipedia.org/w/index.php?title=Factory_method_pattern&oldid=363482142#Javascript


1
Die Factory-Methode scheint eine gute Lösung zu sein. Verwechseln Sie sie nur nicht mit der Verwendung einer separaten Factory-Klasse, was in diesem Anwendungsfall wahrscheinlich völlig irrelevant ist.
Simon Groenewolt

1
Dieser Link führt nirgendwo hin. Auf dieser Seite befindet sich kein Javascript-Anker.
Rob

3

Beantworten, da diese Frage zuerst in Google zurückgegeben wird, die Antworten jedoch veraltet sind.

Sie können Destructuring-Objekte als Konstruktorparameter in ES6 verwenden

Hier ist das Muster:

Sie können nicht mehrere Konstruktoren haben, aber Sie können Destrukturierungs- und Standardwerte verwenden, um das zu tun, was Sie wollen.

export class myClass {

  constructor({ myArray = [1, 2, 3], myString = 'Hello World' }) {

    // ..
  }
}

Und Sie können dies tun, wenn Sie einen 'parameterlosen' Konstruktor unterstützen möchten.

export class myClass {

      constructor({myArray = [1, 2, 3], myString = 'Hello World'} = {}) {

        // ..
      }
}

2
export default class Order {

    static fromCart(cart) {
        var newOrder = new Order();
        newOrder.items = cart.items;
        newOrder.sum = cart.sum;

        return newOrder;
    }

    static fromOrder(id, order) {
        var newOrder = new Order();
        newOrder.id = id;
        newOrder.items = order.items;
        newOrder.sum = order.sum;

        return newOrder;
    }
}

Useges:

  var newOrder = Order.fromCart(cart)
  var newOrder = Order.fromOrder(id, oldOrder)

0

Dies ist das Beispiel für mehrere Konstruktoren in der Programmierung in HTML5 mit JavaScript und CSS3 - Exam Ref .

function Book() {
    //just creates an empty book.
}


function Book(title, length, author) {
    this.title = title;
    this.Length = length;
    this.author = author;
}

Book.prototype = {
    ISBN: "",
    Length: -1,
    genre: "",
    covering: "",
    author: "",
    currentPage: 0,
    title: "",

    flipTo: function FlipToAPage(pNum) {
        this.currentPage = pNum;
    },

    turnPageForward: function turnForward() {
        this.flipTo(this.currentPage++);
    },

    turnPageBackward: function turnBackward() {
        this.flipTo(this.currentPage--);
    }
};

var books = new Array(new Book(), new Book("First Edition", 350, "Random"));

Und Unveränderlichkeit? Durch die Verwendung von Prototypen werden Eigenschaften öffentlich gemacht, oder?
Gabriel Simas

6
Das ist falsch. Ihre zweite Konstruktordefinition überschreibt die erste. Wenn Sie also später new Book () aufrufen, rufen Sie den zweiten Konstruktor auf, wobei alle Parameterwerte auf undefiniert gesetzt sind.
ErroneousFatality
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.