多个构造函数的JavaScript模式


124

我的实例需要其他构造函数。常见的模式是什么?


请更具体一点。您想要具有不同参数集的构造函数吗?
gblazex

您可以在JavaScript中使用多个构造函数吗?
Doug Hauf 2014年

是的也没有@DougHauf。是的,因为bobince提供的答案提供了一种传递等效行为的方法。否,因为如果要使用多个不同的构造函数(每个函数都共享同一个原型对象),将如何设置原型对象的构造函数属性(因为构造函数属性只能指向一个构造函数)。
Moika Turns'Apr

3
所有这些答案都是过时的/不理想的。我懒得输入答案,但是您可以将一个对象传递给函数和构造函数,然后像使用参数一样使用键,例如:function ({ oneThing = 7, otherThing = defaultValue } = {}) { }= {}我输入的额外内容是我最近学到的另一个技巧,以防万一您希望用户根本不传入任何对象并使用所有默认值。
安德鲁

1
后续:以下是解决此问题的一些好方法:stackoverflow.com/a/32626901/1599699 stackoverflow.com/a/41051984/1599699 stackoverflow.com/a/48287734/1599699我特别喜欢最后一个多构造样的支持,使用静态工厂功能构造(return new this();return new this.otherStaticFactoryFunction();,等)!
安德鲁

Answers:


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有两个构造函数-但都带有一个参数,它是一个字符串:-)
Alex Dean

1
您可以为该特定示例添加更多示例代码吗?
Doug Hauf 2014年

@DougHauf,您好,Crockford的书“ JavaScript:好的零件”中有一个名为“对象说明符”的部分,许多示例在线上都涉及到。
Moika Turns'Apr

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

10
我不明白 您可以在实际使用的地方添加代码吗?您是否每次都要从fromComponents调用?因为那不是真正的构造函数,而是一个辅助函数。@bobince的答案似乎更准确。
hofnarwillie 2014年

1
@hofnarwillie虽然不是一个确切的构造函数,但是通过使用静态方法,它执行的过程非常相似,即var foobarObj = Foobar.fromComponents(foo,bar); 这是创建具有备用参数的新对象所需的全部。
内森·威廉姆斯

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
这是最好的答案。其他人则依靠解析数组并做大量不必要的工作。
布兰·汤森

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

我现在也使用以下方法
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是否应该带有单词function。
Doug Hauf 2014年

在第一个Foo()中的}之后是否必须使用分号。
Doug Hauf 2014年

谢谢道格,我做了零钱。
laughingbovine

您确定这可行吗?因为无法访问对象的属性,所以无法将链接new Foo()和调用链接init在一起。我不得不跑步var a = new Foo(); a.init_1('constructor 1');
米莉·史密斯

@MillieSmith我承认我已经有一段时间没有写JS了,但是我只是将此代码粘贴到Chrome JS控制台中,并且从新到初始化的链都起作用了。
laughingbovine

3

有时,参数的默认值足以满足多个构造函数的需要。并且当这还不够时,我尝试将大多数构造函数包装到随后调用的init(other-params)函数中。还可以考虑使用工厂概念来制作可以有效创建所需其他对象的对象。

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


1
工厂方法似乎是一个很好的解决方案-请确保不要将其与单独的工厂类混淆,这在本使用案例中可能是完全不相关的。
Simon Groenewolt

1
该链接无处。该页面上没有Javascript锚。
罗布2012年

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

用途:

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

和不变性?使用原型将属性公开,对吗?
加百利·西马斯

6
那是不对的。您的第二个构造函数定义将覆盖第一个构造函数,因此,当您稍后调用new Book()时,您将在所有参数值均设置为undefined的情况下调用第二个构造函数。
ErrongousFatality
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.