有人可以弄清楚Javascript中的构造函数和工厂函数之间的区别。

何时使用一个而不是另一个?

#1 楼

基本区别在于,构造函数与new关键字一起使用(这会使JavaScript自动创建一个新对象,将该函数内的this设置为该对象,然后返回该对象):
br />
工厂函数就像“常规”函数一样被调用:返回某个对象的新实例:如果它仅返回布尔值或其他内容,则不会将其称为“工厂”函数。这不会像new那样自动发生,但是在某些情况下确实提供了更大的灵活性。

在一个非常简单的示例中,上面引用的功能可能类似于:
var objFromConstructor = new ConstructorFunction();


当然,您可以使工厂函数比简单的例子复杂得多。取决于某些参数。

评论


“(编辑:这可能是个问题,因为如果没有新功能,该功能仍会运行,但不会按预期运行)。”仅当您尝试使用“ new”调用工厂函数或尝试使用“ this”关键字分配给实例时,这才是问题。否则,您只需创建一个新的任意对象,然后将其返回即可。没问题,只是一种不同的,更灵活的处理方式,更少的样板且不会将实例化细节泄漏到API中。

–埃里克·埃利奥特(Eric Elliott)
2013年1月5日14:10



我想指出的是,这两种情况的示例(构造函数与工厂函数)应该是一致的。工厂函数的示例未包含工厂返回的对象的someMethod,因此此处有些模糊。在工厂函数内部,如果只是执行var obj = {...,someMethod:function(){},...},那将导致返回的每个对象都持有someMethod的不同副本,这是我们可能不知道的想。那就是在工厂函数中使用new和prototype会有所帮助的地方。

–巴拉特·哈特里(Bharat Khatri)
2014年5月5日22:16

正如您已经提到的,有些人尝试使用工厂函数只是因为他们不想将错误留在人们忘记使用构造函数的情况下;我认为这可能是需要了解如何用工厂函数示例替换构造函数的地方,而我认为示例之间的一致性是必需的。无论如何,答案是足够的。这只是我想提出的一点,而不是我以任何方式降低答案的质量。

–巴拉特·哈特里(Bharat Khatri)
2014年3月15日10:14



对我来说,Factory函数的最大优点是您可以获得更好的封装和数据隐藏,这在某些应用程序中可能很有用。如果使每个实例属性和方法公开并容易被用户修改没有问题,那么除非您不喜欢某些人喜欢的“ new”关键字,否则我认为Constructor函数更合适。

– devius
2014年9月29日上午11:36

@Federico-工厂方法不必只返回一个普通对象。他们可以在内部使用new,也可以使用Object.create()创建具有特定原型的对象。

– nnnnnn
2015年6月18日,0:55



#2 楼

使用构造函数的好处


大多数书籍都教您使用构造函数和new
this指的是新对象
有些人喜欢var myFoo = new Foo();的读取方式。

缺点



实例化的详细信息(通过new要求)泄漏到了调用API中,因此所有调用者都与构造函数实现紧密耦合。如果需要工厂的额外灵活性,则必须重构所有调用方(公认的例外情况,而不是规则)。
忘记new是一个常见错误,您应该强烈考虑添加样板检查以确保正确调用了构造函数(if (!(this instanceof Foo)) { return new Foo() })。编辑:自ES6(ES2015)起,您不会忘记带有new构造函数的class,否则该构造函数将引发错误。在我看来,不应该这样。您实际上已经使instanceof要求短路,这意味着您可以消除缺点1。但是,除了名称之外,您几乎只具有工厂功能,带有附加的样板代码,大写字母和较不灵活的new上下文。

构造函数破坏了开放/封闭原则

但我主要担心的是,它违反了开放/封闭原则。您开始导出构造器,用户开始使用构造器,然后逐渐意识到需要工厂的灵活性(例如,将实现切换为使用对象池,在执行上下文之间实例化或使用原型OO具有更大的继承灵活性)。

但是,您仍然受困。如果不破坏用new调用构造函数的所有代码,就无法进行更改。例如,您不能切换到使用对象池来提高性能。

另外,使用构造函数会给您一个欺骗性的this,它在执行上下文中不起作用,并且如果构造函数原型被换出也不起作用。如果您开始从构造函数返回new,然后切换到导出任意对象,则必须执行此操作才能在构造函数中启用类似工厂的行为。

的好处使用factory


更少的代码-不需要样板。
您可以返回任意对象,并使用任意原型-使您更加灵活地创建实现相同的API。例如,可以创建HTML5和Flash播放器实例的媒体播放器,或者可以发出DOM事件或Web套接字事件的事件库。工厂还可以在执行上下文中实例化对象,利用对象池,并允许使用更灵活的原型继承模型。
您将不需要从工厂转换为构造函数,因此重构永远不会问题。
使用instanceof不会产生歧义。别。 (这会使this表现不佳,请参阅下一点。)
new的行为与往常一样-因此您可以使用它来访问父对象(例如,在this内,this引用player.create(),就像其他任何对象方法调用将按预期进行thisplayer也会重新分配call如果将原型存储在父对象上,这可能是动态交换功能并为对象实例化启用非常灵活的多态性的好方法。关于是否要大写的含糊不清。不要。皮棉工具会抱怨,然后您会尝试使用apply,然后您将撤消上述好处。
有些人喜欢thisnew读取。

缺点


var myFoo = foo();的行为不符合预期(请参见上文)解决方案:请勿使用。
var myFoo = foo.create();不引用新对象(相反,如果构造函数以点符号或方括号符号调用,例如foo.bar()-new引用this-就像其他所有JavaScript方法一样-请参见好处)。 br />

评论


从什么意义上说,构造函数使调用者与实现紧密耦合?就构造函数参数而言,它们甚至需要传递给工厂函数,以便使用它们并在其中调用适当的构造函数。

–巴拉特·哈特里(Bharat Khatri)
2014年5月5日15:00

关于“打开/关闭”的违规:这不是全部依赖注入吗?如果A需要B,则A调用new B()或A调用BFactory.create(),它们两者都引入了耦合。另一方面,如果您在合成根目录中为A提供B的实例,则A完全不需要了解如何实例化B。我觉得构造函数和工厂都有用途。构造函数用于简单实例化,工厂用于更复杂的实例化。但是在两种情况下,注入依赖项都是明智的。

– Stefan Billiet
2014年3月18日上午10:51

DI适合注入状态:配置,域对象等。这对其他所有东西来说都是过分杀伤力的。

–埃里克·埃利奥特(Eric Elliott)
2014年3月18日在17:14

大惊小怪的是,要求新违反开放/封闭原则。参见medium.com/javascript-scene/…进行的讨论超出了这些注释所允许的范围。

–埃里克·埃利奥特(Eric Elliott)
2014年11月8日在2:05

因为任何函数都可以在JavaScript中返回新对象,而且很多函数都不需要new关键字就可以返回,所以我不认为new关键字确实可以提供任何其他可读性。海事组织,为了使呼叫者能够键入更多内容,跳到圈里似乎很愚蠢。

–埃里克·埃利奥特(Eric Elliott)
16年8月27日在23:15

#3 楼

构造函数返回您调用的类的实例。工厂函数可以返回任何东西。当您需要返回任意值或类具有较大的设置过程时,可以使用工厂函数。

#4 楼

构造函数示例
function User(name) {
  this.name = name;
  this.isAdmin = false;
}

let user = new User("Jack");



new创建在User.prototype上原型化的对象,并以创建的对象作为User值调用this


new将其操作数的参数表达式视为可选:
     let user = new User;

将导致new调用不带参数的User


new返回其创建的对象,除非构造函数返回一个对象值,否则将返回该对象值。这是一个边缘情况,在大多数情况下可以忽略。优点和缺点由构造函数创建的对象从构造函数的prototype属性继承属性,并使用构造函数上的instanceOf运算符。
如果在使用完构造函数后动态更改构造函数的prototype属性的值,则上述行为可能会失败。这样做很少见,如果使用class关键字创建了构造函数,则无法更改。
可以使用extends关键字扩展构造函数。
构造函数不能将null作为错误返回值。由于它不是对象数据类型,因此new将忽略它。
工厂函数示例
function User(name, age) {
  return {
    name,
    age,
  }
};

let user = User("Tom", 23);

此处调用了工厂函数而未使用new。如果函数的参数及其返回的对象类型,则该函数完全负责直接或间接使用。在此示例中,它返回一个简单的[Object object],该对象具有从参数中设置的一些属性。轻松地向调用者隐藏对象创建的实现复杂性。这对于浏览器中的本机代码功能特别有用。
工厂函数不必总是返回相同类型的对象,甚至可以返回null作为错误指示符。
在简单情况下,工厂函数可以结构和含义要简单。
返回的对象通常不会继承自工厂函数的prototype属性,而不会继承自falseinstanceOf factoryFunction。工厂函数使用的构造函数的extends属性。

评论


这是针对同一主题针对此问题发布的最新答案,

–拖拉机
18/12/26在5:15

不仅是“ null”,而且“ new”也将忽略构造函数返回的任何前提数据类型。

– Vishal
6月3日,11:51



#5 楼

工厂“总是”更好。使用面向对象的语言时,


决定合同(方法及其将要执行的操作)
创建公开这些方法的接口(在javascript中,您没有接口因此您需要提出一些检查实现的方法)
创建一个工厂,该工厂返回所需的每个接口的实现。

实现(使用new创建的实际对象)不是暴露给工厂用户/消费者。这意味着工厂开发人员可以在不违反合同的前提下扩展并创建新的实现...并且这允许工厂消费者仅从新API中受益而无需更改其代码...如果他们使用new并出现了“ new”实现,那么他们必须去更改使用“ new”的每一行以使用“ new”实现...在工厂中,其代码不会改变...

要素-比所有其他要素都要好-spring框架完全基于此构想。

评论


工厂如何解决必须更换每条生产线的问题?

–代码名称杰克
6月15日下午6:33

#6 楼

工厂是抽象的一层,就像所有抽象一样,它们也有一定的成本。遇到基于工厂的API时,弄清楚给定API的工厂对于API使用者而言可能是一个挑战。对于构造函数,可发现性是微不足道的。

在决策者和工厂之间做出决定时,您需要确定复杂性是否因其利益而合理。

值得一提的是,通过返回this或undefined以外的内容,Javascript构造函数可以是任意工厂。因此,在js中,您可以兼得两者-可发现的API和对象池/缓存。

评论


在JavaScript中,使用构造函数的成本高于使用工厂的成本,因为JS中的任何函数都可以返回新对象。构造函数通过以下方法增加了复杂性:要求新的,更改其行为,更改返回值,连接原型ref,启用instanceof(这是谎言,不应用于此目的)。表面上看,所有这些都是“功能”。实际上,它们会损害您的代码质量。

–埃里克·埃利奥特(Eric Elliott)
2015年6月17日19:49



#7 楼

对于差异,埃里克·埃利奥特(Eric Elliott)很好地阐明了,

但是对于第二个问题: br />
如果您来自面向对象的背景,那么构造函数对您来说看起来更自然。
这样,您就不会忘记使用new关键字。