複数のコンストラクターのJavaScriptパターン


124

インスタンスごとに異なるコンストラクタが必要です。そのための一般的なパターンは何ですか?


もう少し具体的にしてください。異なるパラメーターセットのコンストラクターが必要ですか?
gblazex 2010

JavaScriptで複数のコンストラクターを使用できますか?
Doug Hauf 14

はい、いいえ@DougHauf。はい。bobinceが提供する答えは、同等の動作を提供する方法を提供するためです。いいえ。複数の異なるコンストラクター関数(それぞれが同じプロトタイプオブジェクトを共有する)が必要な場合、プロトタイプオブジェクトのコンストラクタープロパティはどのように設定されるのですか(コンストラクタープロパティは1つのコンストラクター関数しか指定できないため)。
Moikaが

3
これらすべての答えは古く、理想的ではありません。私は答えをタイプするのが面倒ですが、関数やコンストラクターにオブジェクトを渡し、引数と同じようにキーを使用できますfunction ({ oneThing = 7, otherThing = defaultValue } = {}) { }。例:余分な= {}私は、ユーザーのすべての中で何のオブジェクトを渡していないと、デフォルトのすべてを使用する可能性を望む場合には、最近学んだ別のトリックがあるに入れます。
Andrew

1
フォロー:ここでは、この問題を解決するために、いくつかの良い方法があります。stackoverflow.com/a/32626901/1599699 stackoverflow.com/a/41051984/1599699 stackoverflow.com/a/48287734/1599699私は本当の最後の1の特に好きですサポートのような複数のコンストラクタ、(コンストラクタのような静的ファクトリ関数を使用してreturn new this();return new this.otherStaticFactoryFunction();など、)!
アンドリュー

回答:


120

JavaScriptには、メソッドやコンストラクターを含め、関数のオーバーロードはありません。

関数に渡すパラメーターの数とタイプに応じて、関数の動作を変更したい場合は、手動で嗅ぐ必要があります。JavaScriptは、宣言された引数の数より多いまたは少ない関数を喜んで呼び出します。

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

arguments配列のようにアクセスして、追加の引数を渡すこともできます。

より複雑な引数が必要な場合は、それらの一部またはすべてをオブジェクトルックアップ内に配置することをお勧めします。

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

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

Pythonは、デフォルトの引数と名前付き引数を使用して、関数のオーバーロードよりも実用的で優雅な方法でほとんどのユースケースをカバーする方法を示しています。JavaScript、それほどではありません。


2
ありがとう、これは本当に素晴らしいです。2番目のオプションは、複雑な引数がある場合だけでなく、たとえばMyObj({foo: "foo"})plusをサポートするなど、まだ識別が難しい単純な引数がある場合にも役立ちますMyObj({bar: "bar"})。MyObjには2つのコンストラクタがありますが、どちらも文字列である1つの引数を取ります:-)
Alex Dean

1
この特定の例のコード例にさらに追加できますか?
Doug Hauf 14

こんにちは@ DougHauf、Crockfordの本「JavaScript:The Good Parts」には、この「オブジェクト指定子」という名前のセクションがあり、多くの例がオンラインで参照されています。
Moikaが

29

これはどうやって見つけるの?

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

Foobar.prototype = {
    foobar: null
};

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

2
新しいthis(foobar);を返す 動作しません。戻り時に新しいFoobar(foobar);に変更します。そしてすべてが正しい仕事です。
isxaker 2013年

10
わかりません。実際に使用している場所にコードを追加できますか?毎回fromComponentsを呼び出す必要がありますか?なぜならそれは本当にコンストラクタではなく、むしろヘルパー関数だからです。@bobinceの答えは、より正確に思えます。
hofnarwillie 14

1
@hofnarwillie正確なコンストラクタではありませんが、静的メソッドを使用することで、かなり似たようなパフォーマンスを実現します。代替引数を使用して新しいオブジェクトを作成するために必要なすべてです。
ネイサンウィリアムズ

19

そのクラスのインスタンスを返す静的メソッドでクラスを使用できます

    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)

このパターンを使用すると、マルチコンストラクターを作成できます


3
これが最良の答えです。その他は、配列の解析と不必要な作業の束に頼っています。
Blane Townsend、

13

ボビンスの答えのように手動で行うような気がしなかったので、jQueryのプラグインオプションパターンを完全に取り除きました。

これがコンストラクタです:

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

インスタンス化のさまざまな方法を次に示します。

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

そして、それが作ったものです:

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

node.jsでも同じパターンを次のように展開
Jacob McKay

10

eruciformの答えにさらに進むと、new呼び出しをinitメソッドにチェーンできます。

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

つまり、基本的にここでは、オブジェクトFooを取得して、プロトタイプ関数でinit_1およびinit_2パラメータを呼び出しています。init_1とinit_2にそれらと一緒にword関数がある場合。
Doug Hauf 14

最初のFoo()の}の後にセミコロンが必要ですか?
Doug Hauf 14

ダグに感謝、私は変更を加えた。
laughingbovine

これで問題ありませんか?オブジェクトのプロパティにアクセスできなかったためnew Foo()、チェーンとinit一緒に呼び出すことができませんでした。私は走らなければなりませんでしたvar a = new Foo(); a.init_1('constructor 1');
ミリー・スミス

@MillieSmith私は今しばらくJSを作成していないことを認めます...しかし、このコードをChrome JSコンソールに貼り付けただけで、newからinitへのチェーンが機能しました。
laughingbovine

3

複数のコンストラクターの場合、パラメーターのデフォルト値で十分な場合があります。そして、それで不十分な場合は、コンストラクター機能のほとんどを、後で呼び出されるinit(other-params)関数にラップしようとします。また、ファクトリコンセプトを使用して、必要な他のオブジェクトを効果的に作成できるオブジェクトを作成することも検討してください。

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


1
ファクトリーメソッドは良い解決策のように感じられます。別のファクトリークラスの使用と混同しないように注意してください。これは、おそらくこのユースケースでは完全に無関係です。
Simon Groenewolt 2010

1
そのリンクはどこにも行きません。そのページにはJavaScriptアンカーがありません。
Rob

3

この質問は最初にグーグルで返されるので答えますが、答えは古くなっています。

ES6では、Destructuringオブジェクトをコンストラクタパラメータとして使用できます。

ここにパターンがあります:

複数のコンストラクタを持つことはできませんが、構造化とデフォルト値を使用して必要なことを行うことができます。

export class myClass {

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

    // ..
  }
}

そして、「パラメーターレス」コンストラクターをサポートしたい場合は、これを行うことができます。

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

これは、JavaScriptとCSS3を使用したHTML5でのプログラミングの複数のコンストラクターの例です-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"));

そして不変性?プロトタイプを使用すると、プロパティが公開されます。
Gabriel Simas 2017年

6
不正解です。2番目のコンストラクター定義は最初のコンストラクター定義をオーバーライドするため、後でnew Book()を呼び出すときに、すべてのパラメーターの値を未定義に設定して2番目のコンストラクターを呼び出します。
ErroneousFatality
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.