Pattern JavaScript per più costruttori


124

Ho bisogno di diversi costruttori per le mie istanze. Qual è un modello comune per questo?


sii un po 'più specifico per favore. vuoi costruttori con diversi set di parametri?
gblazex

Puoi avere più di un costruttore in Javascript?
Doug Hauf

Sì e no @DougHauf. Sì perché la risposta fornita da bobince fornisce un modo per fornire un comportamento equivalente. No, perché se volessi più funzioni di costruzione distinte (ciascuna condividendo lo stesso oggetto prototipo) come verrebbe impostata la proprietà del costruttore dell'oggetto prototipo (poiché la proprietà del costruttore può puntare solo a una funzione del costruttore).
Moika compie il

3
Tutte queste risposte sono vecchie / non ideali. Sono troppo pigro per digitare fino una risposta, ma si può passare un oggetto intorno a funzioni e costruttori e quindi utilizzare i tasti proprio come si farebbe argomenti, ad esempio: function ({ oneThing = 7, otherThing = defaultValue } = {}) { }. L'extra che = {}ho inserito è un altro trucco che ho imparato di recente, nel caso in cui desideri la possibilità che l'utente non passi alcun oggetto e utilizzi tutti i valori predefiniti.
Andrew

1
Seguito: ecco alcuni buoni modi per risolvere questo problema: stackoverflow.com/a/32626901/1599699 stackoverflow.com/a/41051984/1599699 stackoverflow.com/a/48287734/1599699 Sono particolarmente affezionato all'ultimo per vero multiple-costruttore-come il supporto, utilizzando le funzioni factory statici come costruttori ( return new this();, return new this.otherStaticFactoryFunction();, ecc)!
Andrew

Risposte:


120

JavaScript non ha il sovraccarico delle funzioni, incluso per metodi o costruttori.

Se vuoi che una funzione si comporti in modo diverso a seconda del numero e del tipo di parametri che le passi, dovrai annusarli manualmente. JavaScript chiamerà felicemente una funzione con più o meno del numero di argomenti dichiarato.

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);
}

Puoi anche accedere argumentscome un array per ottenere ulteriori argomenti passati.

Se hai bisogno di argomenti più complessi, può essere una buona idea inserirne alcuni o tutti all'interno di una ricerca di oggetti:

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

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

Python dimostra come gli argomenti predefiniti e denominati possono essere usati per coprire la maggior parte dei casi d'uso in un modo più pratico e grazioso rispetto al sovraccarico di funzioni. JavaScript, non così tanto.


2
Grazie, è davvero carino. Direi che la seconda opzione è utile non solo quando si hanno argomenti complessi, ma anche argomenti semplici ma difficili da distinguere, ad esempio il supporto di MyObj({foo: "foo"})plus MyObj({bar: "bar"}). MyObj ha due costruttori, ma entrambi accettano un argomento, che è una stringa :-)
Alex Dean

1
Puoi aggiungere altro al codice di esempio per questo particolare esempio.
Doug Hauf

Ciao @DougHauf, il libro di Crockford 'JavaScript: The Good Parts' ha una sezione su questo chiamato 'Object Specifiers', molti esempi si riferiscono ad esso online.
Moika compie il

29

Come trovi questo?

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

Foobar.prototype = {
    foobar: null
};

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

2
restituisci nuovo this (foobar); non funziona. Cambio al ritorno nuovo Foobar (foobar); e tutto funziona correttamente.
isxaker

10
Non lo capisco. Puoi aggiungere il codice dove lo stai effettivamente utilizzando? Dovrai chiamare daComponents ogni volta? Perché non è veramente un costruttore, ma piuttosto una funzione di supporto. La risposta di @ bobince sembra quindi più accurata.
hofnarwillie

1
@hofnarwillie Sebbene non sia un costruttore esatto, utilizzando un metodo statico si comporta in modo abbastanza simile, ad esempio var foobarObj = Foobar.fromComponents (foo, bar); è tutto ciò che serve per creare un nuovo oggetto con gli argomenti alternativi.
Nathan Williams

21

puoi usare la classe con metodi statici che restituiscono un'istanza di quella classe

    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)

con questo modello puoi creare multi costruttore


3
Questa è la migliore risposta. Gli altri ricorrono all'analisi degli array e fanno un sacco di lavoro non necessario.
Blane Townsend

13

Non avevo voglia di farlo a mano come nella risposta di bobince, quindi ho semplicemente strappato completamente il modello delle opzioni del plugin di jQuery.

Ecco il costruttore:

//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;
}

Ecco diversi modi di istanziazione:

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']});

Ed ecco cosa ha fatto:

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]}

Ho inserito lo stesso modello anche in node.js ora con: npmjs.com/package/extend
Jacob McKay

10

Andando oltre con la risposta di eruciforme, puoi concatenare la tua newchiamata nel tuo initmetodo.

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');

Quindi fondamentalmente quello che stai facendo qui è prendere l'oggetto Foo e quindi chiamare i parametri init_1 e init_2 con le funzioni prototipo. Il tuo init_1 e init_2 hanno la parola function con loro.
Doug Hauf

deve esserci un punto e virgola dopo} nel primo Foo ().
Doug Hauf

Grazie Doug, ho apportato la modifica.
laughingbovine

Sei sicuro che funzioni? Non sono stato in grado di concatenare new Foo()e chiamare initinsieme perché non ero in grado di accedere alle proprietà sugli oggetti. Ho dovuto correrevar a = new Foo(); a.init_1('constructor 1');
Millie Smith

@MillieSmith Devo ammettere che non scrivo JS da un po 'di tempo ... ma ho appena incollato questo codice nella console JS di Chrome e la catena da new a init ha funzionato.
laughingbovine

3

A volte, i valori predefiniti per i parametri sono sufficienti per più costruttori. E quando ciò non è sufficiente, provo a racchiudere la maggior parte delle funzionalità del costruttore in una funzione init (altri parametri) che viene chiamata in seguito. Considera anche l'utilizzo del concetto di fabbrica per creare un oggetto che possa creare efficacemente gli altri oggetti desiderati.

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


1
Il metodo Factory sembra una buona soluzione: assicurati solo di non confonderlo con l'uso di una classe factory separata, che probabilmente è completamente irrilevante in questo caso d'uso.
Simon Groenewolt

1
Quel collegamento non va da nessuna parte. Non ci sono ancora Javascript in quella pagina.
Rob il

3

Rispondere perché questa domanda viene restituita prima in google ma le risposte ora sono obsolete.

È possibile utilizzare oggetti Destructuring come parametri del costruttore in ES6

Ecco lo schema:

Non puoi avere più costruttori, ma puoi usare la destrutturazione e i valori predefiniti per fare quello che vuoi.

export class myClass {

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

    // ..
  }
}

E puoi farlo se vuoi supportare un costruttore "senza parametri".

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

Questo è l'esempio fornito per più costruttori nella programmazione in HTML5 con JavaScript e 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"));

E l'immutabilità? l'utilizzo di prototype rende pubbliche le proprietà, giusto?
Gabriel Simas

6
Non è corretto. La tua seconda definizione di costruttore sovrascrive la prima, quindi quando chiami new Book () in seguito, chiami il secondo costruttore con tutti i valori dei parametri impostati su undefined.
ErroneousFatality
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.