รูปแบบ JavaScript สำหรับตัวสร้างหลายตัว


124

ฉันต้องการตัวสร้างที่แตกต่างกันสำหรับอินสแตนซ์ของฉัน รูปแบบทั่วไปสำหรับสิ่งนั้นคืออะไร?


โปรดเจาะจงให้มากขึ้น คุณต้องการตัวสร้างที่มีชุดพารามิเตอร์ต่างกันหรือไม่?
gblazex

คุณสามารถมีตัวสร้างมากกว่าหนึ่งตัวใน Javascript ได้หรือไม่?
Doug Hauf

ใช่และไม่ใช่ @DougHauf ใช่เพราะคำตอบที่ Bobince ให้ไว้เป็นวิธีการแสดงพฤติกรรมที่เท่าเทียมกัน ไม่ใช่เพราะถ้าคุณต้องการฟังก์ชันตัวสร้างที่แตกต่างกันหลายฟังก์ชัน (แต่ละตัวแบ่งใช้อ็อบเจ็กต์ต้นแบบเดียวกัน) คุณสมบัติคอนสตรัคเตอร์ของอ็อบเจ็กต์ต้นแบบจะได้รับการตั้งค่าอย่างไร (เนื่องจากคุณสมบัติคอนสตรัคเตอร์สามารถชี้ไปที่ฟังก์ชันคอนสตรัคเตอร์ได้เพียงฟังก์ชันเดียวเท่านั้น)
Moika เปิด

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"}) MyObj({bar: "bar"})MyObj มีตัวสร้างสองตัว - แต่ทั้งคู่ใช้อาร์กิวเมนต์เดียวซึ่งก็คือสตริง :-)
Alex Dean

1
คุณสามารถเพิ่มโค้ดตัวอย่างสำหรับตัวอย่างนี้ได้หรือไม่
Doug Hauf

สวัสดี @DougHauf หนังสือ 'JavaScript: The Good Parts' ของ Crockford มีส่วนในชื่อ 'Object Specifiers' ซึ่งมีตัวอย่างมากมายที่อ้างถึงทางออนไลน์
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
ส่งคืนสิ่งนี้ใหม่ (foobar); ไม่ทำงาน ฉันเปลี่ยนเมื่อส่งคืน Foobar ใหม่ (foobar); และทั้งหมดทำงานถูกต้อง
isxaker

10
ฉันไม่เข้าใจ คุณสามารถเพิ่มรหัสที่คุณใช้งานจริงได้หรือไม่? คุณจะต้องโทรหาจากส่วนประกอบทุกครั้งหรือไม่? เพราะนั่นไม่ใช่ตัวสร้างอย่างแท้จริง แต่เป็นฟังก์ชันตัวช่วย คำตอบของ @ Bobince ดูเหมือนจะถูกต้องมากขึ้น
hofnarwillie

1
@hofnarwillie ในขณะที่ไม่ได้เป็นตัวสร้างที่แน่นอนโดยใช้วิธีการแบบคงที่มันทำงานค่อนข้างคล้ายกันเช่น var foobarObj = Foobar.fromComponents (foo, bar); คือทั้งหมดที่คุณต้องสร้างวัตถุใหม่ด้วยอาร์กิวเมนต์ทางเลือก
Nathan Williams

21

คุณสามารถใช้คลาสด้วยวิธีการคงที่ที่ส่งคืนอินสแตนซ์ของคลาสนั้น

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

ฉันได้รีดรูปแบบเดียวกันใน node.js แล้วด้วย: npmjs.com/package/extend
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

จะต้องมีอัฒภาคหลังเครื่องหมาย} ใน Foo () ตัวแรกหรือไม่
Doug Hauf

ขอบคุณดั๊กฉันทำการเปลี่ยนแปลง
laughingbovine

แน่ใจหรือว่าใช้งานได้ ฉันไม่สามารถเชื่อมโยงnew Foo()และเรียกร้องให้ไปinitด้วยกันได้เพราะฉันไม่สามารถเข้าถึงคุณสมบัติของวัตถุได้ ฉันต้องวิ่งvar a = new Foo(); a.init_1('constructor 1');
Millie Smith

@MillieSmith ฉันจะยอมรับว่าฉันไม่ได้เขียน JS มาสักพักแล้ว ... แต่ฉันเพิ่งวางรหัสนี้ลงในคอนโซล Chrome JS และห่วงโซ่จากใหม่ถึงเริ่มทำงาน
laughingbovine

3

บางครั้งค่าเริ่มต้นสำหรับพารามิเตอร์ก็เพียงพอสำหรับตัวสร้างหลายตัว และเมื่อไม่เพียงพอฉันพยายามรวมฟังก์ชันตัวสร้างส่วนใหญ่ไว้ในฟังก์ชัน init (พารามิเตอร์อื่น ๆ ) ที่ถูกเรียกใช้ในภายหลัง ลองพิจารณาใช้แนวคิดของโรงงานเพื่อสร้างวัตถุที่สามารถสร้างวัตถุอื่น ๆ ที่คุณต้องการได้อย่างมีประสิทธิภาพ

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


1
วิธีการของโรงงานให้ความรู้สึกเหมือนเป็นทางออกที่ดี - อย่าสับสนกับการใช้คลาสโรงงานแยกต่างหากซึ่งอาจไม่เกี่ยวข้องอย่างสมบูรณ์ในกรณีการใช้งานนี้
Simon Groenewolt

1
ลิงก์นั้นไปที่ไหนเลย ไม่มีจุดยึด Javascript ในหน้านั้น
Rob

3

ตอบเนื่องจากคำถามนี้ถูกส่งกลับก่อนใน Google แต่ตอนนี้คำตอบล้าสมัยแล้ว

คุณสามารถใช้วัตถุทำลายล้างเป็นพารามิเตอร์ตัวสร้างใน ES6

นี่คือรูปแบบ:

คุณไม่สามารถมีตัวสร้างหลายตัวได้ แต่คุณสามารถใช้การทำลายโครงสร้างและค่าเริ่มต้นเพื่อทำสิ่งที่คุณต้องการได้

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

นี่คือตัวอย่างที่ได้รับสำหรับการก่อสร้างหลายแห่งในการเขียนโปรแกรมใน HTML5 ด้วย JavaScript และ CSS3 - สอบ 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

6
นั่นไม่ถูกต้อง คำจำกัดความตัวสร้างที่สองของคุณจะแทนที่คำจำกัดความแรกดังนั้นเมื่อคุณเรียก Book ใหม่ () ในภายหลังคุณจะเรียกตัวสร้างที่สองโดยตั้งค่าพารามิเตอร์ทั้งหมดเป็นไม่ได้กำหนด
ErroneousFatality
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.