여러 생성자를위한 JavaScript 패턴


124

내 인스턴스에 대해 다른 생성자가 필요합니다. 그것에 대한 일반적인 패턴은 무엇입니까?


좀 더 구체적으로 말씀해주세요. 다른 매개 변수 세트를 가진 생성자를 원하십니까?
gblazex 2010

Javascript에 둘 이상의 생성자를 가질 수 있습니까?
Doug Hauf 2014

예, 아니오 @DougHauf. 예, bobince가 제공하는 답변이 동등한 행동을 제공하는 방법을 제공하기 때문입니다. 아니요, 여러 개의 고유 한 생성자 함수 (각각 동일한 프로토 타입 객체를 공유)를 원한다면 프로토 타입 객체의 생성자 속성이 어떻게 설정 될까요 (생성자 속성은 하나의 생성자 함수 만 가리킬 수 있기 때문).
Moika Turns

3
이 모든 답변은 오래되었거나 이상적이지 않습니다. 답을 입력하기에는 너무 게으르지 만 함수와 생성자에 객체를 전달한 다음 인수처럼 키를 사용할 수 있습니다 function ({ oneThing = 7, otherThing = defaultValue } = {}) { }. 예 : . = {}내가 거기에 넣은 추가 사항 은 사용자가 객체를 전혀 전달하지 않고 모든 기본값을 사용할 가능성을 원할 경우 최근에 배운 또 다른 트릭입니다.
Andrew

1
후속 조치 :이 문제를 해결하는 몇 가지 좋은 방법은 다음과 같습니다. stackoverflow.com/a/32626901/1599699 stackoverflow.com/a/41051984/1599699 stackoverflow.com/a/48287734/1599699 저는 특히 마지막 문제를 좋아합니다. 지원 형 멀티 생성자, (생성자 정적 팩토리 함수를 사용하여 return new this();, return new this.otherStaticFactoryFunction();등)!
Andrew

답변:


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
고마워, 정말 멋지다. 두 번째 옵션은 복잡한 인수가있을 때뿐만 아니라 단순하지만 구별하기 어려운 인수 (예 : MyObj({foo: "foo"})plus 지원)도 유용하다고 말할 수 있습니다 MyObj({bar: "bar"}). 하지만 문자열 :-) 모두 포획 한 인수 -하여 MyObj는 두 개의 생성자가
알렉스 딘

1
이 특정 예제의 예제 코드에 더 많은 것을 추가 할 수 있습니까?
Doug Hauf 2014

안녕하세요 @DougHauf, Crockford의 저서 'JavaScript : The Good Parts'에는 'Object Specifiers'라는 섹션이 있습니다. 온라인에서 많은 예제를 참조합니다.
Moika Turns

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
return new this (foobar); 작동하지 않습니다. 나는 return new Foobar (foobar); 모든 것이 올바르게 작동합니다.
isxaker

10
이해가 안 돼요. 실제로 사용하고있는 곳에 코드를 추가 할 수 있습니까? 매번 fromComponents를 호출해야합니까? 이것은 진정한 생성자가 아니라 도우미 함수이기 때문입니다. @bobince의 대답은 더 정확 해 보입니다.
hofnarwillie

1
@hofnarwillie 정확한 생성자는 아니지만 정적 메서드를 사용하면 상당히 유사한 작업을 수행합니다. 즉 var foobarObj = Foobar.fromComponents (foo, bar); 대체 인수로 새 개체를 만드는 데 필요한 모든 것입니다.
Nathan Williams

20

해당 클래스의 인스턴스를 반환하는 정적 메서드와 함께 클래스를 사용할 수 있습니다.

    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

bobince의 답변처럼 손으로하고 싶지 않았기 때문에 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]}


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에 함수라는 단어가 있어야합니다.
Doug Hauf 2014

첫 번째 Foo ()에서} 뒤에 세미콜론이 있어야합니까?
Doug Hauf 2014

고마워 Doug, 내가 변경했습니다.
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

1
그 링크는 아무데도 가지 않습니다. 해당 페이지에는 Javascript 앵커가 없습니다.
Rob

3

이 질문이 Google에서 먼저 반환 되었기 때문에 답변했지만 답변은 이제 구식입니다.

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-Exam Ref를 사용한 HTML5 프로그래밍의 여러 생성자에 대해 제공된 예제 입니다.

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

6
그것은 틀 렸습니다. 두 번째 생성자 정의가 첫 번째 생성자를 재정의하므로 나중에 new Book ()을 호출 할 때 모든 매개 변수의 값이 undefined로 설정된 두 번째 생성자를 호출합니다.
ErroneousFatality
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.