Modèle JavaScript pour plusieurs constructeurs

124

J'ai besoin de différents constructeurs pour mes instances. Quel est le modèle courant pour cela?

codéholique
la source
soyez un peu plus précis s'il vous plaît. vous voulez des constructeurs avec différents jeux de paramètres?
gblazex
Pouvez-vous avoir plus d'un constructeur en Javascript?
Doug Hauf le
Oui et non @DougHauf. Oui car la réponse fournie par bobince permet de délivrer un comportement équivalent. Non, car si vous vouliez plusieurs fonctions de constructeur distinctes (chacune partageant le même objet prototype), comment la propriété constructeur de l'objet prototype serait-elle définie (puisque la propriété constructeur ne peut pointer que sur une fonction constructeur).
Moika fête le
3
Toutes ces réponses sont anciennes / pas idéales. Je suis trop paresseux pour taper une réponse, mais vous pouvez passer un objet autour des fonctions et des constructeurs, puis utilisez les touches comme vous le feriez arguments, par exemple: function ({ oneThing = 7, otherThing = defaultValue } = {}) { }. Le supplément que = {}j'ai mis là-dedans est une autre astuce que j'ai apprise récemment, au cas où vous voudriez que l'utilisateur ne passe aucun objet et utilise toutes les valeurs par défaut.
Andrew le
1
Suivi: Voici quelques bonnes façons de résoudre ce problème: stackoverflow.com/a/32626901/1599699 stackoverflow.com/a/41051984/1599699 stackoverflow.com/a/48287734/1599699 J'aime particulièrement le dernier pour vrai plusieurs constructeur comme support, en utilisant les fonctions d'usine statiques comme les constructeurs ( return new this();, return new this.otherStaticFactoryFunction();, etc.)!
Andrew

Réponses:

120

JavaScript n'a pas de surcharge de fonctions, y compris pour les méthodes ou les constructeurs.

Si vous souhaitez qu'une fonction se comporte différemment en fonction du nombre et des types de paramètres que vous lui passez, vous devrez les renifler manuellement. JavaScript appellera volontiers une fonction avec plus ou moins que le nombre d'arguments déclaré.

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

Vous pouvez également accéder argumentssous forme de tableau pour obtenir d'autres arguments transmis.

Si vous avez besoin d'arguments plus complexes, il peut être judicieux de les placer tout ou partie dans une recherche d'objet:

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

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

Python montre comment les arguments par défaut et nommés peuvent être utilisés pour couvrir la plupart des cas d'utilisation d'une manière plus pratique et plus élégante que la surcharge de fonctions. JavaScript, pas tellement.

bobince
la source
2
Merci, c'est vraiment sympa. Je dirais que la deuxième option est utile non seulement lorsque vous avez des arguments complexes, mais aussi des arguments simples mais difficiles à distinguer, par exemple en soutenant MyObj({foo: "foo"})plus MyObj({bar: "bar"}). MyObj a deux constructeurs - mais les deux prennent un argument, qui est une chaîne :-)
Alex Dean
1
Pouvez-vous ajouter plus à l'exemple de code pour cet exemple particulier.
Doug Hauf
Salut @DougHauf, le livre de Crockford «JavaScript: The Good Parts» a une section sur ce intitulé «Object Specifiers», de nombreux exemples y font référence en ligne.
Moika fête le
29

Comment trouvez-vous celui-ci?

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

Foobar.prototype = {
    foobar: null
};

Foobar.fromComponents = function(foo, bar) {
    var foobar = foo + bar;
    return new this(foobar);
};
codéholique
la source
2
retourne nouveau ce (foobar); ne fonctionne pas. Je change au retour un nouveau Foobar (foobar); et tout fonctionne correctement.
isxaker
10
Je ne comprends pas. Pouvez-vous ajouter le code là où vous l'utilisez réellement? Allez-vous devoir appeler des composants à chaque fois? Parce que ce n'est pas vraiment un constructeur, mais plutôt une fonction d'assistance. La réponse de @ bobince semble alors plus précise.
hofnarwillie
1
@hofnarwillie Bien que n'étant pas tout à fait un constructeur exact, en utilisant une méthode statique, il fonctionne assez similaire à savoir var foobarObj = Foobar.fromComponents (foo, bar); est tout ce dont vous avez besoin pour créer un nouvel objet avec les arguments alternatifs.
Nathan Williams
21

vous pouvez utiliser une classe avec des méthodes statiques qui retournent une instance de cette 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)

avec ce modèle, vous pouvez créer plusieurs constructeurs

mahdi shahbazi
la source
3
C'est la meilleure réponse. Les autres ont recours à l'analyse des tableaux et à un travail inutile.
Blane Townsend
13

Je n'avais pas envie de le faire à la main comme dans la réponse de bobince, alors j'ai complètement déchiré le modèle d'options de plugin de jQuery.

Voici le constructeur:

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

Voici différentes méthodes d'instanciation:

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

Et voici ce que cela a fait:

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]}
Jacob McKay
la source
J'ai aussi roulé le même modèle dans node.js maintenant avec: npmjs.com/package/extend
Jacob McKay
10

En allant plus loin avec la réponse d'eruciform, vous pouvez enchaîner votre newappel dans votre initméthode.

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');
rire bovin
la source
Donc, fondamentalement, ce que vous faites ici est de prendre l'objet Foo, puis d'appeler les paramètres init_1 et init_2 avec les fonctions prototypes. Si vos init_1 et init_2 ont le mot function avec eux.
Doug Hauf
doit-il y avoir un point-virgule après le} dans le premier Foo ().
Doug Hauf
Merci Doug, j'ai fait le changement.
rire
Êtes-vous sûr que cela fonctionne? Je n'ai pas pu enchaîner new Foo()et appeler initensemble parce que je ne pouvais pas accéder aux propriétés des objets. Je devais courirvar a = new Foo(); a.init_1('constructor 1');
Millie Smith
@MillieSmith J'admets que je n'ai pas écrit JS depuis un moment maintenant ... mais je viens de coller ce code dans la console Chrome JS et la chaîne de nouveau à init a fonctionné.
ritingbovine
3

Parfois, les valeurs par défaut des paramètres sont suffisantes pour plusieurs constructeurs. Et quand cela ne suffit pas, j'essaie d'envelopper la plupart des fonctionnalités du constructeur dans une fonction init (other-params) qui est appelée par la suite. Pensez également à utiliser le concept d'usine pour créer un objet capable de créer efficacement les autres objets de votre choix.

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

éruciforme
la source
1
La méthode d'usine semble être une bonne solution - assurez-vous simplement de ne pas la confondre avec l'utilisation d'une classe d'usine distincte, ce qui n'est probablement pas du tout pertinent dans ce cas d'utilisation.
Simon Groenewolt
1
Ce lien ne mène nulle part. Il n'y a pas d'ancre Javascript sur cette page.
Rob
3

Répondre car cette question est renvoyée en premier dans google mais les réponses sont désormais obsolètes.

Vous pouvez utiliser des objets de destruction comme paramètres de constructeur dans ES6

Voici le modèle:

Vous ne pouvez pas avoir plusieurs constructeurs, mais vous pouvez utiliser la déstructuration et les valeurs par défaut pour faire ce que vous voulez.

export class myClass {

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

    // ..
  }
}

Et vous pouvez le faire si vous souhaitez prendre en charge un constructeur «sans paramètre».

export class myClass {

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

        // ..
      }
}
DalSoft
la source
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;
    }
}

Utilisations:

  var newOrder = Order.fromCart(cart)
  var newOrder = Order.fromOrder(id, oldOrder)
Arsen Ablaev
la source
0

C'est l'exemple donné pour plusieurs constructeurs en Programmation en HTML5 avec JavaScript et CSS3 - Réf d'examen .

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"));
Simon Stanford
la source
Et l'immuabilité? l'utilisation d'un prototype rend les propriétés publiques, non?
Gabriel Simas
6
C'est inexact. Votre deuxième définition de constructeur remplace la première, donc lorsque vous appelez new Book () plus tard, vous appelez le deuxième constructeur avec les valeurs de tous les paramètres définies sur undefined.
ErroneousFatality