Pola JavaScript untuk banyak konstruktor

124

Saya memerlukan konstruktor yang berbeda untuk instance saya. Apa pola umum untuk itu?

codeholic
sumber
harap lebih spesifik. Anda ingin konstruktor dengan set parameter yang berbeda?
gblazex
Bisakah Anda memiliki lebih dari satu konstruktor dalam Javascript?
Doug Hauf
Ya dan tidak @DougHauf. Ya karena jawaban yang diberikan oleh bobince memberikan cara untuk menyampaikan perilaku yang setara. Tidak, karena jika Anda menginginkan beberapa fungsi konstruktor yang berbeda (masing-masing berbagi objek prototipe yang sama) bagaimana properti konstruktor dari objek prototipe akan disetel (karena properti konstruktor hanya dapat menunjuk ke satu fungsi konstruktor).
Moika Ternyata
3
Semua jawaban ini kuno / tidak ideal. Aku terlalu malas untuk mengetik jawaban, tetapi Anda dapat melewati sebuah objek di sekitar untuk fungsi dan konstruktor dan kemudian gunakan tombol seperti kamu akan argumen, misalnya: function ({ oneThing = 7, otherThing = defaultValue } = {}) { }. Ekstra yang = {}saya masukkan di sana adalah trik lain yang saya pelajari baru-baru ini, jika Anda ingin kemungkinan pengguna tidak melewatkan objek sama sekali dan menggunakan semua default.
Andrew
1
Tindak lanjut: Berikut adalah beberapa cara yang baik untuk mengatasi masalah ini: stackoverflow.com/a/32626901/1599699 stackoverflow.com/a/41051984/1599699 stackoverflow.com/a/48287734/1599699 Saya sangat menyukai yang terakhir untuk benar multiple-konstruktor-seperti dukungan, menggunakan fungsi pabrik statis sebagai konstruktor ( return new this();, return new this.otherStaticFactoryFunction();, dll)!
Andrew

Jawaban:

120

JavaScript tidak memiliki kelebihan beban, termasuk untuk metode atau konstruktor.

Jika Anda ingin suatu fungsi berperilaku berbeda bergantung pada jumlah dan jenis parameter yang Anda berikan, Anda harus mengendusnya secara manual. JavaScript akan dengan senang hati memanggil fungsi dengan lebih banyak atau lebih sedikit dari jumlah argumen yang dideklarasikan.

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

Anda juga dapat mengakses argumentssebagai array-like untuk mendapatkan argumen lebih lanjut yang diteruskan.

Jika Anda membutuhkan argumen yang lebih kompleks, ada baiknya untuk meletakkan beberapa atau semuanya di dalam pencarian objek:

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

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

Python mendemonstrasikan bagaimana argumen default dan bernama dapat digunakan untuk mencakup sebagian besar kasus penggunaan dengan cara yang lebih praktis dan anggun daripada overloading fungsi. JavaScript, tidak terlalu banyak.

bobince
sumber
2
Terima kasih, ini sangat bagus. Saya akan mengatakan pilihan kedua berguna tidak hanya ketika Anda memiliki argumen yang kompleks, tetapi juga argumen yang sederhana namun sulit untuk dibedakan, misalnya mendukung MyObj({foo: "foo"})plus MyObj({bar: "bar"}). MyObj memiliki dua konstruktor - tetapi keduanya mengambil satu argumen, yaitu string :-)
Alex Dean
1
Bisakah Anda menambahkan lebih banyak kode contoh untuk contoh khusus ini.
Doug Hauf
Hai @DougHauf, buku Crockford 'JavaScript: The Good Parts' memiliki bagian bernama 'Penentu Objek', banyak contoh merujuknya secara online.
Moika Ternyata
29

Bagaimana Anda menemukan yang ini?

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

Foobar.prototype = {
    foobar: null
};

Foobar.fromComponents = function(foo, bar) {
    var foobar = foo + bar;
    return new this(foobar);
};
codeholic
sumber
2
kembalikan baru ini (foobar); tidak bekerja. Saya mengganti foobar baru (foobar); dan semuanya bekerja dengan benar.
pembuat isxaker
10
Saya tidak mengerti. Dapatkah Anda menambahkan kode di tempat yang sebenarnya Anda gunakan? Apakah Anda harus menelepon fromComponents setiap saat? Karena itu bukan benar-benar konstruktor, melainkan fungsi pembantu. Jawaban @ bobince tampaknya lebih akurat.
hofnarwillie
1
@hofnarwillie Meskipun bukan konstruktor yang tepat, dengan menggunakan metode statis performanya cukup mirip yaitu var foobarObj = Foobar.fromComponents (foo, bar); adalah semua yang Anda butuhkan untuk membuat objek baru dengan argumen alternatif.
Nathan Williams
21

Anda bisa menggunakan kelas dengan metode statis yang mengembalikan instance kelas itu

    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)

dengan pola ini Anda dapat membuat multi konstruktor

mahdi shahbazi
sumber
3
Inilah jawaban terbaik. Yang lain menggunakan parsing array dan melakukan banyak pekerjaan yang tidak perlu.
Blane Townsend
13

Tidak merasa ingin melakukannya dengan tangan seperti pada jawaban bobince, jadi saya baru saja merobek pola opsi plugin jQuery.

Inilah konstruktornya:

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

Berikut cara berbeda dalam pembuatan contoh:

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

Dan inilah yang membuatnya:

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
sumber
Saya telah menggulung pola yang sama di node.js juga sekarang dengan: npmjs.com/package/extend
Jacob McKay
10

Lebih jauh dengan jawaban eruciform, Anda dapat newmenghubungkan panggilan Anda ke dalam initmetode Anda .

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');
laughingbovine
sumber
Jadi pada dasarnya apa yang Anda lakukan di sini adalah mengambil objek Foo dan kemudian memanggil parameter init_1 dan init_2 dengan fungsi prototipe. Haruskah init_1 dan init_2 Anda memiliki fungsi kata dengan mereka.
Doug Hauf
apakah harus ada titik koma setelah} di Foo () pertama.
Doug Hauf
Terima kasih Doug, saya membuat perubahan.
laughingbovine
Apakah Anda yakin ini berhasil? Saya tidak dapat new Foo()menghubungkan dan melakukan panggilan initbersama karena saya tidak dapat mengakses properti pada objek. Saya harus larivar a = new Foo(); a.init_1('constructor 1');
Millie Smith
@MillieSmith Saya akui saya belum menulis JS untuk beberapa waktu sekarang ... tapi saya baru saja menempelkan kode ini ke konsol Chrome JS dan rantai dari baru ke init berfungsi.
laughingbovine
3

Terkadang, nilai default untuk parameter cukup untuk banyak konstruktor. Dan ketika itu tidak mencukupi, saya mencoba untuk membungkus sebagian besar fungsionalitas konstruktor menjadi fungsi init (other-params) yang dipanggil setelahnya. Pertimbangkan juga untuk menggunakan konsep pabrik untuk membuat sebuah objek yang secara efektif dapat membuat objek lain yang Anda inginkan.

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

eruciform
sumber
1
Metode pabrik terasa seperti solusi yang baik - pastikan untuk tidak mengacaukannya dengan penggunaan kelas pabrik yang terpisah, yang mungkin sama sekali tidak relevan dalam kasus penggunaan ini.
Simon Groenewolt
1
Tautan itu tidak mengarah ke mana pun. Tidak ada javascript di halaman itu.
Rob
3

Menjawab karena pertanyaan ini dikembalikan pertama kali di google tetapi jawaban sekarang sudah ketinggalan zaman.

Anda dapat menggunakan objek Destructuring sebagai parameter konstruktor di ES6

Berikut polanya:

Anda tidak dapat memiliki banyak konstruktor, tetapi Anda dapat menggunakan nilai destruktur dan default untuk melakukan apa yang Anda inginkan.

export class myClass {

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

    // ..
  }
}

Dan Anda dapat melakukan ini jika Anda ingin mendukung konstruktor 'tanpa parameter'.

export class myClass {

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

        // ..
      }
}
DalSoft
sumber
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)
Arsen Ablaev
sumber
0

Ini adalah contoh yang diberikan untuk beberapa konstruktor dalam Pemrograman dalam HTML5 dengan JavaScript dan CSS3 - Referensi Ujian .

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
sumber
Dan Kekekalan? menggunakan prototipe membuat properti menjadi publik, bukan?
Gabriel Simas
6
Itu tidak benar. Definisi konstruktor kedua Anda menimpa yang pertama, jadi ketika Anda memanggil Book () baru nanti, Anda memanggil konstruktor kedua dengan semua nilai parameter disetel ke tidak ditentukan.
ErroneousFatality