Wzorzec JavaScript dla wielu konstruktorów

124

Potrzebuję różnych konstruktorów dla moich instancji. Jaki jest wspólny wzorzec?

codeholic
źródło
proszę być bardziej szczegółowe. chcesz konstruktorów z różnymi zestawami parametrów?
gblazex,
Czy możesz mieć więcej niż jeden konstruktor w Javascript?
Doug Hauf
Tak i nie @DougHauf. Tak, ponieważ odpowiedź udzielona przez bobince zapewnia sposób na zapewnienie równoważnego zachowania. Nie, ponieważ gdybyś chciał mieć wiele różnych funkcji konstruktora (każda współdzieląca ten sam obiekt prototypowy), w jaki sposób zostanie ustawiona właściwość konstruktora obiektu prototypowego (ponieważ właściwość konstruktora może wskazywać tylko jedną funkcję konstruktora).
Moika zamienia się
3
Wszystkie te odpowiedzi są stare / nieidealne. Jestem zbyt leniwy, aby wpisać odpowiedź, ale możesz przekazać obiekt do funkcji i konstruktorów, a następnie użyć klawiszy tak jak argumentów, np function ({ oneThing = 7, otherThing = defaultValue } = {}) { }. : . Dodatkową = {}rzeczą, którą tam umieściłem, jest kolejna sztuczka, której nauczyłem się ostatnio, na wypadek, gdybyś chciał mieć możliwość przekazania przez użytkownika żadnego obiektu i używania wszystkich wartości domyślnych.
Andrew,
1
Kontynuacja: Oto kilka dobrych sposobów rozwiązania tego problemu: stackoverflow.com/a/32626901/1599699 stackoverflow.com/a/41051984/1599699 stackoverflow.com/a/48287734/1599699 Szczególnie podoba mi się ten ostatni za prawdę wielokrotnego konstruktor podobnego wsparcia, stosując jako statyczne funkcje fabryczne konstruktorów ( return new this();, return new this.otherStaticFactoryFunction();, itd.)!
Andrew

Odpowiedzi:

120

JavaScript nie ma przeciążania funkcji, w tym dla metod lub konstruktorów.

Jeśli chcesz, aby funkcja zachowywała się inaczej w zależności od liczby i typów parametrów, które do niej przekazujesz, musisz ją ręcznie wąchać. JavaScript szczęśliwie wywoła funkcję z większą lub mniejszą niż zadeklarowaną liczbą argumentów.

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

Możesz również uzyskać dostęp argumentsjako tablicę, aby uzyskać dalsze przekazane argumenty.

Jeśli potrzebujesz bardziej złożonych argumentów, dobrym pomysłem może być umieszczenie niektórych lub wszystkich z nich w wyszukiwaniu obiektów:

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

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

Python demonstruje, jak domyślne i nazwane argumenty mogą być używane do opisywania większości przypadków użycia w bardziej praktyczny i wdzięczny sposób niż przeciążanie funkcji. JavaScript, nie tak bardzo.

bobince
źródło
2
Dzięki, to naprawdę miłe. Powiedziałbym, że druga opcja jest przydatna nie tylko wtedy, gdy masz złożone argumenty, ale także argumenty proste, ale trudne do rozróżnienia, np. Popierający MyObj({foo: "foo"})plus MyObj({bar: "bar"}). MyObj ma dwa konstruktory - ale oba przyjmują jeden argument, którym jest ciąg znaków :-)
Alex Dean
1
Czy możesz dodać więcej do przykładowego kodu dla tego konkretnego przykładu.
Doug Hauf,
Cześć @DougHauf, w książce Crockforda „JavaScript: The Good Parts” znajduje się sekcja zatytułowana „Object Specifiers”, wiele przykładów odnosi się do tego w Internecie.
Moika zamienia się
29

Jak to znajdujesz?

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

Foobar.prototype = {
    foobar: null
};

Foobar.fromComponents = function(foo, bar) {
    var foobar = foo + bar;
    return new this(foobar);
};
codeholic
źródło
2
return new this (foobar); nie działa. Po powrocie zmieniam nowy Foobar (foobar); i wszystko działa poprawnie.
isxaker
10
Nie rozumiem. Czy możesz dodać kod tam, gdzie go faktycznie używasz? Czy za każdym razem będziesz musiał dzwonić z firmy Components? Ponieważ to nie jest prawdziwy konstruktor, ale raczej funkcja pomocnicza. Odpowiedź @ bobince wydaje się wtedy dokładniejsza.
hofnarwillie
1
@hofnarwillie Chociaż nie jest to dokładny konstruktor, przy użyciu metody statycznej działa dość podobnie, np. var foobarObj = Foobar.fromComponents (foo, bar); to wszystko, czego potrzebujesz, aby utworzyć nowy obiekt z alternatywnymi argumentami.
Nathan Williams,
21

możesz używać class ze statycznymi metodami, które zwracają instancję tej klasy

    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)

za pomocą tego wzorca możesz stworzyć konstruktor multi

mahdi shahbazi
źródło
3
To najlepsza odpowiedź. Inni uciekają się do analizowania tablic i wykonywania zbędnej pracy.
Blane Townsend
13

Nie miałem ochoty robić tego ręcznie, jak w odpowiedzi bobince, więc po prostu całkowicie oderwałem wzorzec opcji wtyczki jQuery.

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

Oto różne sposoby tworzenia instancji:

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

A oto, co to spowodowało:

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
źródło
Wywaliłem ten sam wzorzec w node.js teraz z: npmjs.com/package/extend
Jacob McKay
10

Idąc dalej z odpowiedzią eruciform, możesz newpołączyć swoje wywołanie ze swoją initmetodą.

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');
roześmiany bydło
źródło
Więc w zasadzie to, co tutaj robisz, to pobranie obiektu Foo, a następnie wywołanie parametrów init_1 i init_2 z funkcjami prototypowymi. Czy twoje init_1 i init_2 mają ze sobą funkcję słowa.
Doug Hauf,
czy po znaku} musi występować średnik w pierwszym Foo ().
Doug Hauf,
Dzięki Doug, dokonałem zmiany.
laughingbovine
Czy na pewno to działa? Nie byłem w stanie new Foo()połączyć i wywołać initrazem, ponieważ nie byłem w stanie uzyskać dostępu do właściwości obiektów. Musiałem biecvar a = new Foo(); a.init_1('constructor 1');
Millie Smith
@MillieSmith Przyznaję, że od jakiegoś czasu nie pisałem JS ... ale właśnie wkleiłem ten kod do konsoli Chrome JS i łańcuch od new do init zadziałał.
laughingbovine
3

Czasami domyślne wartości parametrów wystarczą dla wielu konstruktorów. A kiedy to nie wystarczy, próbuję zawinąć większość funkcji konstruktora w funkcję init (inne parametry), która jest wywoływana później. Rozważ również użycie koncepcji fabryki, aby utworzyć obiekt, który może skutecznie tworzyć inne pożądane obiekty.

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

eruciform
źródło
1
Metoda fabryczna wydaje się dobrym rozwiązaniem - pamiętaj tylko, aby nie mylić jej z użyciem oddzielnej klasy fabrycznej, co prawdopodobnie jest zupełnie nieistotne w tym przypadku użycia.
Simon Groenewolt,
1
Ten link prowadzi donikąd. Na tej stronie nie ma kotwicy Javascript.
Rob
3

Odpowiadam, ponieważ to pytanie jest zwracane jako pierwsze w Google, ale odpowiedzi są teraz nieaktualne.

Możesz użyć Destrukturyzacja obiektów jako parametrów konstruktora w ES6

Oto wzór:

Nie możesz mieć wielu konstruktorów, ale możesz użyć destrukturyzacji i wartości domyślnych, aby robić to, co chcesz.

export class myClass {

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

    // ..
  }
}

Możesz to zrobić, jeśli chcesz obsługiwać konstruktor „bez parametrów”.

export class myClass {

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

        // ..
      }
}
DalSoft
źródło
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;
    }
}

Zastosowania:

  var newOrder = Order.fromCart(cart)
  var newOrder = Order.fromOrder(id, oldOrder)
Arsen Ablaev
źródło
0

To jest przykład podany dla wielu konstruktorów w Programowaniu w HTML5 z JavaScript i CSS3 - nr ref . Egzaminu .

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
źródło
A niezmienność? użycie prototypu powoduje upublicznienie właściwości, prawda?
Gabriel Simas
6
To jest niepoprawne. Twoja druga definicja konstruktora zastępuje pierwszą, więc kiedy później wywołujesz metodę new Book (), wywołujesz drugi konstruktor z wartościami wszystkich parametrów ustawionymi na niezdefiniowane.
ErroneousFatality