Javascript 构造函数与工厂函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8698726/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Constructor function vs Factory functions
提问by Sinan
Can someone clarify the difference between a constructor function and a factory function in Javascript.
有人可以澄清 Javascript 中构造函数和工厂函数之间的区别。
When to use one instead of the other?
什么时候使用一个而不是另一个?
采纳答案by nnnnnn
The basic difference is that a constructor function is used with the new
keyword (which causes JavaScript to automatically create a new object, set this
within the function to that object, and return the object):
基本区别在于构造函数与new
关键字一起使用(这会导致 JavaScript 自动创建一个新对象,this
在函数内设置为该对象,并返回该对象):
var objFromConstructor = new ConstructorFunction();
A factory function is called like a "regular" function:
工厂函数被称为“常规”函数:
var objFromFactory = factoryFunction();
But for it to be considered a "factory" it would need to return a new instance of some object: you wouldn't call it a "factory" function if it just returned a boolean or something. This does not happen automatically like with new
, but it does allow more flexibility for some cases.
但是要使其被视为“工厂”,它需要返回某个对象的新实例:如果它只是返回一个布尔值或其他东西,您就不会将其称为“工厂”函数。这不会像 with 那样自动发生new
,但它确实为某些情况提供了更大的灵活性。
In a really simple example the functions referenced above might look something like this:
在一个非常简单的示例中,上面引用的函数可能如下所示:
function ConstructorFunction() {
this.someProp1 = "1";
this.someProp2 = "2";
}
ConstructorFunction.prototype.someMethod = function() { /* whatever */ };
function factoryFunction() {
var obj = {
someProp1 : "1",
someProp2 : "2",
someMethod: function() { /* whatever */ }
};
// other code to manipulate obj in some way here
return obj;
}
Of course you can make factory functions much more complicated than that simple example.
当然,您可以使工厂函数比那个简单的例子复杂得多。
Some people prefer to use factory functions for everything just because they don't like having to remember to use new
(EDIT: and this can be a problem because without new
the function will still run but not as expected). I don't see that as an advantage: new
is a core part of the language so to me deliberately avoiding it is a bit arbitrary - might as well avoid other keywords like else
.
有些人喜欢对所有事情都使用工厂函数,只是因为他们不想记住使用new
(编辑:这可能是一个问题,因为没有new
该函数仍然会运行,但不会像预期的那样)。我不认为这是一个优势:new
是语言的核心部分,所以对我来说故意避免它有点武断 - 也可以避免其他关键字,如else
.
One advantage to factory functions is when the object to be returned could be of several different types depending on some parameter.
工厂函数的一个优点是当要返回的对象根据某些参数可以是几种不同的类型时。
回答by Eric Elliott
Benefits of using constructors
使用构造函数的好处
Most books teach you to use constructors and
new
this
refers to the new objectSome people like the way
var myFoo = new Foo();
reads.
大多数书籍教你使用构造函数和
new
this
引用新对象有些人喜欢阅读的方式
var myFoo = new Foo();
。
Drawbacks
缺点
Details of instantiation get leaked into the calling API (via the
new
requirement), so all callers are tightly coupled to the constructor implementation. If you ever need the additional flexibility of the factory, you'll have to refactor all callers (admittedly the exceptional case, rather than the rule).Forgetting
new
is such a common bug, you should strongly consider adding a boilerplate check to ensure that the constructor is called correctly (if (!(this instanceof Foo)) { return new Foo() }
). EDIT: Since ES6 (ES2015) you can't forgetnew
with aclass
constructor, or the constructor will throw an error.If you do the
instanceof
check, it leaves ambiguity as to whether or notnew
is required. In my opinion, it shouldn't be. You've effectively short circuited thenew
requirement, which means you could erase drawback #1. But then you've just got a factory function in all but name, with additional boilerplate, a capital letter, and less flexiblethis
context.
实例化的细节会泄露到调用 API 中(通过
new
需求),因此所有调用者都与构造函数实现紧密耦合。如果您需要工厂的额外灵活性,您将不得不重构所有调用者(不可否认的是例外情况,而不是规则)。遗忘
new
是如此常见的错误,您应该强烈考虑添加样板检查以确保正确调用构造函数 (if (!(this instanceof Foo)) { return new Foo() }
)。编辑:既然ES6(ES2015),你可别忘了new
用class
构造函数或构造函数将抛出一个错误。如果您进行
instanceof
检查,则对于是否new
需要进行检查会产生歧义。在我看来,它不应该是。您已经有效地缩短了new
要求,这意味着您可以消除缺点 #1。但是,除了name之外,您只有一个工厂函数,带有额外的样板、大写字母和不太灵活的this
上下文。
Constructors break the Open / Closed Principle
构造函数打破了开闭原则
But my main concern is that it violates the open/closed principle. You start out exporting a constructor, users start using the constructor, then down the road you realize you need the flexibility of a factory, instead (for instance, to switch the implementation to use object pools, or to instantiate across execution contexts, or to have more inheritance flexibility using prototypal OO).
但我主要担心的是它违反了开放/封闭原则。你开始导出一个构造函数,用户开始使用构造函数,然后你意识到你需要工厂的灵活性,而不是(例如,切换实现以使用对象池,或跨执行上下文实例化,或使用原型 OO 具有更多的继承灵活性)。
You're stuck, though. You can't make the change without breaking all the code that calls your constructor with new
. You can't switch to using object pools for performance gains, for instance.
不过,你被卡住了。你不能在不破坏所有调用构造函数的代码的情况下进行更改new
。例如,您不能切换到使用对象池来提高性能。
Also, using constructors gives you a deceptive instanceof
that doesn't work across execution contexts, and doesn't work if your constructor prototype gets swapped out. It will also fail if you start out returning this
from your constructor, and then switch to exporting an arbitrary object, which you'd have to do to enable factory-like behavior in your constructor.
此外,使用构造函数会给你一个instanceof
在执行上下文中不起作用的欺骗性,如果你的构造函数原型被换出也不起作用。如果您开始this
从构造函数返回,然后切换到导出任意对象,它也会失败,您必须这样做才能在构造函数中启用类似工厂的行为。
Benefits of using factories
使用工厂的好处
Less code - no boilerplate required.
You can return any arbitrary object, and use any arbitrary prototype - giving you more flexibility to create various types of objects which implement the same API. For example, a media player that can create instances of both HTML5 and flash players, or an event library which can emit DOM events or web socket events. Factories can also instantiate objects across execution contexts, take advantage of object pools, and allow for more flexible prototypal inheritance models.
You'd never have a need to convert from a factory to a constructor, so refactoring will never be an issue.
No ambiguity about using
new
. Don't. (It will makethis
behave badly, see next point).this
behaves as it normally would - so you can use it to access the parent object (for example, insideplayer.create()
,this
refers toplayer
, just like any other method invocation would.call
andapply
also reassignthis
, as expected. If you store prototypes on the parent object, that can be a great way to dynamically swap out functionality, and enable very flexible polymorphism for your object instantiation.No ambiguity about whether or not to capitalize. Don't. Lint tools will complain, and then you'll be tempted to try to use
new
, and then you'll undo the benefit described above.Some people like the way
var myFoo = foo();
orvar myFoo = foo.create();
reads.
更少的代码 - 不需要样板。
您可以返回任意对象,并使用任意原型 - 使您可以更灵活地创建实现相同 API 的各种类型的对象。例如,可以创建 HTML5 和 Flash 播放器实例的媒体播放器,或者可以发出 DOM 事件或 Web 套接字事件的事件库。工厂还可以跨执行上下文实例化对象,利用对象池,并允许更灵活的原型继承模型。
您永远不需要从工厂转换为构造函数,因此重构永远不会成为问题。
关于使用没有歧义
new
。别。(这会使this
行为变得糟糕,请参阅下一点)。this
表现得像往常一样 - 所以你可以用它来访问父对象(例如, insideplayer.create()
,this
引用player
,就像任何其他方法调用一样。call
并且apply
也重新分配this
,如预期的那样。如果你在父对象上存储原型,那可以是动态交换功能的好方法,并为您的对象实例化启用非常灵活的多态性。关于是否大写没有歧义。别。Lint 工具会抱怨,然后您会被诱惑尝试使用
new
,然后您将撤消上述好处。有些人喜欢这种方式
var myFoo = foo();
或var myFoo = foo.create();
阅读。
Drawbacks
缺点
new
doesn't behave as expected (see above). Solution: don't use it.this
doesn't refer to the new object (instead, if the constructor is invoked with dot notation or square bracket notation, e.g. foo.bar() -this
refers tofoo
- just like every other JavaScript method -- see benefits).
new
行为不符合预期(见上文)。解决方法:不要使用。this
不引用新对象(相反,如果使用点表示法或方括号表示法调用构造函数,例如 foo.bar() -this
引用foo
- 就像所有其他 JavaScript 方法一样 - 请参阅好处)。
回答by Ignacio Vazquez-Abrams
A constructor returns an instance of the class you call it on. A factory function can return anything. You would use a factory function when you need to return arbitrary values or when a class has a large setup process.
构造函数返回您调用它的类的实例。工厂函数可以返回任何东西。当您需要返回任意值或类具有大型设置过程时,您将使用工厂函数。
回答by traktor53
A Constructor function example
构造函数示例
function User(name) {
this.name = name;
this.isAdmin = false;
}
let user = new User("Hyman");
new
creates an object prototyped onUser.prototype
and callsUser
with the created object as itsthis
value.new
treats an argument expression for its operand as optional:let user = new User;
would cause
new
to callUser
with no arguments.new
returns the object it created, unless the constructor returns an object value, which is returned instead. This is an edge case which for the most part can be ignored.
new
创建一个原型对象User.prototype
并User
使用创建的对象作为其this
值进行调用。new
将其操作数的参数表达式视为可选:let user = new User;
会导致
new
调用User
不带任何参数。new
返回它创建的对象,除非构造函数返回一个对象 value,而是返回它。这是一个边缘情况,在大多数情况下可以忽略。
Pros and Cons
利弊
Objects created by constructor functions inherit properties from the constructor's prototype
property, and return true using the instanceOf
operator on the constructor function.
由构造函数创建的对象从构造函数的prototype
属性继承属性,并使用instanceOf
构造函数上的运算符返回 true 。
The above behaviors can fail if you dynamically change the value of the constructor's prototype
property after having already used the constructor. Doing so is rare, and it can't be changed if the constructor were created using the class
keyword.
如果prototype
在使用构造函数后动态更改构造函数的属性值,则上述行为可能会失败。这样做很少见,如果构造函数是使用class
关键字创建的,则无法更改。
Constructor functions can be extended using the extends
keyword.
可以使用extends
关键字扩展构造函数。
Constructor functions can't return null
as an error value. Since it's not an object data type, it is ignored by new
.
构造函数不能null
作为错误值返回。由于它不是对象数据类型,因此被new
.
A Factory function example
工厂函数示例
function User(name, age) {
return {
name,
age,
}
};
let user = User("Tom", 23);
Here the factory function is called without new
. The function is entirely responsible for the direct or indirect use if its arguments and the type of object it returns. In this example it returns a simple [Object object] with some properties set from arguments.
这里调用工厂函数时没有new
. 该函数完全负责直接或间接使用它的参数和它返回的对象类型。在此示例中,它返回一个简单的 [Object object],其中包含一些从参数设置的属性。
Pros and Cons
利弊
Easily hides the implementation complexities of object creation from the caller. This is particularly useful for native code functions in a browser.
轻松向调用者隐藏对象创建的实现复杂性。这对于浏览器中的本机代码函数特别有用。
The factory function need not always return objects of the same type, and could even return null
as an error indicator.
工厂函数不需要总是返回相同类型的对象,甚至可以null
作为错误指示符返回。
In simple cases, factory functions can be simple in structure and meaning.
在简单的情况下,工厂函数的结构和含义可以很简单。
Objects returned do not generally inherit from the factory function's prototype
property, and return false
from instanceOf factoryFunction
.
返回的对象通常不继承工厂函数的prototype
属性,而是false
从instanceOf factoryFunction
.
The factory function can't be safely extended using the extends
keyword because extended objects would inherit from the factory functions prototype
property instead of from the prototype
property of the constructor used by the factory function.
不能使用extends
关键字安全地扩展工厂函数,因为扩展对象将从工厂函数prototype
属性而不是从prototype
工厂函数使用的构造函数的属性继承。
回答by Colin Saxton
Factories are "always" better. When using object orientated languages then
工厂“总是”更好。当使用面向对象的语言时
- decide on the contract (the methods and what they will do)
- Create interfaces that expose those methods (in javascript you don't have interfaces so you need to come up with some way of checking the implementation)
- Create a factory that returns an implementation of each interface required.
- 决定合同(方法和他们将做什么)
- 创建公开这些方法的接口(在 javascript 中你没有接口,所以你需要想出一些检查实现的方法)
- 创建一个工厂,返回每个所需接口的实现。
The implementations (the actual objects created with new) are not exposed to the factory user/consumer. This means that the factory developer can expand and create new implementations as long as he/she doesn't break the contract...and it allows for the factory consumer to just benefit from the new API without having to change their code...if they used new and a "new" implementation comes along then they have to go and change every line which uses "new" to use the "new" implementation...with the factory their code doesn't change...
实现(用 new 创建的实际对象)不会暴露给工厂用户/消费者。这意味着工厂开发人员可以扩展和创建新的实现,只要他/她不违反合同......并且它允许工厂消费者从新的 API 中受益,而无需更改他们的代码......如果他们使用了 new 并且出现了一个“新”实现,那么他们必须去更改使用“new”的每一行以使用“新”实现......在工厂中,他们的代码不会改变......
Factories - better than all anything else - the spring framework is completely built around this idea.
工厂 - 比其他任何东西都好 - spring 框架完全围绕这个想法构建。
回答by Mostafa
For the differences, Eric Elliott clarified very well,
对于差异,埃里克·埃利奥特 (Eric Elliott) 澄清得很好,
But for the second question:
但是对于第二个问题:
When to use one instead of the other?
什么时候使用一个而不是另一个?
If you are coming from the object-oriented background, Constructor function looks more natural to you.
this way you shouldn't forget to use new
keyword.
如果您来自面向对象的背景,Constructor 函数对您来说看起来更自然。这样你就不应该忘记使用new
关键字。
回答by PeterH
Factories are a layer of abstraction, and like all abstractions they have a.cost in complexity. When encountering a factory based API figuring out what the factory is for a given API can be challenging for the API consumer. With constructors discoverability is trivial.
工厂是一个抽象层,与所有抽象一样,它们在复杂性上也有成本。当遇到基于工厂的 API 时,弄清楚给定 API 的工厂是什么对于 API 使用者来说可能是一个挑战。对于构造函数,可发现性是微不足道的。
When deciding between ctors and factories you need to decide if the complexity is justified by the benefit.
在 ctors 和 factory 之间做出决定时,您需要确定复杂性是否因收益而合理。
Worth noting that Javascript constructors can be arbitrary factories by returning something other than this or undefined. So in js you can get the best of both worlds - discoverable API and object pooling/caching.
值得注意的是,Javascript 构造函数可以通过返回除 this 或 undefined 之外的其他内容来成为任意工厂。因此,在 js 中,您可以两全其美——可发现的 API 和对象池/缓存。