nnn*_*nnn 143
基本区别在于构造函数与new关键字一起使用(这会导致JavaScript自动创建一个新对象,this在该对象的函数内设置,并返回该对象):
var objFromConstructor = new ConstructorFunction();
Run Code Online (Sandbox Code Playgroud)
工厂函数被称为"常规"函数:
var objFromFactory = factoryFunction();
Run Code Online (Sandbox Code Playgroud)
但是要将它视为"工厂",它需要返回某个对象的新实例:如果它只返回一个布尔值或其他东西,则不会将其称为"工厂"函数.这不会像自动一样自动发生new,但它确实为某些情况提供了更大的灵活性.
在一个非常简单的例子中,上面引用的函数可能如下所示:
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;
}
Run Code Online (Sandbox Code Playgroud)
当然,你可以使工厂功能比这个简单的例子复杂得多.
有些人喜欢将工厂功能用于所有事情,因为他们不喜欢不记得使用new(编辑:这可能是一个问题,因为没有new该功能仍将运行但不如预期).我不认为这是一个优势:new是语言的核心部分所以对我来说故意避免它有点武断 - 不妨避免使用其他关键词else.
工厂功能的一个优点是,根据某些参数,要返回的对象可能有几种不同的类型.
Eri*_*ott 104
大多数书籍教你使用构造函数和 new
this 指新对象
有些人喜欢var myFoo = new Foo();读书的方式.
实例化的细节被泄露到调用API中(通过new需求),因此所有调用者都与构造函数实现紧密耦合.如果您需要工厂的额外灵活性,您将不得不重构所有呼叫者(无可否认的是例外情况,而不是规则).
忘记new是一个常见的错误,你应该强烈考虑添加样板检查以确保正确调用构造函数(if (!(this instanceof Foo)) { return new Foo() }).编辑:既然ES6(ES2015),你可别忘了new用class构造函数或构造函数将抛出一个错误.
如果您进行instanceof检查,则会对是否new需要进行模糊处理.在我看来,它不应该.你已经有效地缩短了new要求,这意味着你可以消除缺点#1.但是,除了名称之外,你只有一个工厂函数,还有额外的样板,大写字母和不太灵活的this上下文.
但我主要担心的是它违反了开放/封闭原则.您开始导出构造函数,用户开始使用构造函数,然后您意识到您需要工厂的灵活性,而不是(例如,将实现切换为使用对象池,或跨执行上下文实例化,或者使用原型OO具有更多的继承灵活性.
但是你被卡住了.如果不破坏调用构造函数的所有代码,则无法进行更改new.例如,您无法切换到使用对象池来提高性能.
此外,使用构造函数会为您提供一种instanceof在执行上下文中不起作用的欺骗性,并且如果您的构造函数原型被换出,则不起作用.如果this从构造函数开始返回,然后切换到导出任意对象,它也将失败,您必须这样做才能在构造函数中启用类似工厂的行为.
更少的代码 - 无需样板.
您可以返回任意对象,并使用任意原型 - 为您提供更多灵活性来创建实现相同API的各种类型的对象.例如,可以创建HTML5和Flash播放器实例的媒体播放器,或者可以发出DOM事件或Web套接字事件的事件库.工厂还可以跨执行上下文实例化对象,利用对象池,并允许更灵活的原型继承模型.
您永远不需要从工厂转换为构造函数,因此重构永远不会成为问题.
没有关于使用的歧义new.别.(它会使this表现很糟糕,见下一点).
this行为与通常一样 - 所以你可以使用它来访问父对象(例如,在内部player.create(),this引用player,就像任何其他方法调用一样.call并且apply也this按预期重新分配.如果你将原型存储在父对象上,那么可以是动态交换功能的好方法,并为对象实例化启用非常灵活的多态性.
没有关于是否资本化的含糊不清.别.Lint工具会抱怨,然后你会试图尝试使用new,然后你将撤消上述的好处.
有些人喜欢这种方式var myFoo = foo();或var myFoo = foo.create();阅读.
new行为不符合预期(见上文).解决方案:不要使用它.
this不引用新对象(相反,如果使用点表示法或方括号表示法调用构造函数,例如foo.bar() - this引用foo- 就像所有其他JavaScript方法一样 - 请参阅优点).
function User(name) {
this.name = name;
this.isAdmin = false;
}
let user = new User("Jack");
Run Code Online (Sandbox Code Playgroud)
new创建一个原型对象,User.prototype并User使用创建的对象作为其this值进行调用。
new 将其操作数的自变量表达式视为可选:
let user = new User;
Run Code Online (Sandbox Code Playgroud)
会导致new调用User不带任何参数。
new返回它创建的对象,除非构造函数返回一个object value,而是返回它。这是一个边缘情况,在大多数情况下可以忽略。
由构造函数创建的对象从构造函数的prototype属性继承属性,并使用instanceOf构造函数上的运算符返回true 。
如果您prototype已经在使用构造函数后动态更改了构造函数的属性值,则上述行为可能会失败。这样做很少见,并且如果使用class关键字创建构造函数,则无法更改。
可以使用extends关键字扩展构造函数。
构造函数不能null作为错误值返回。由于它不是对象数据类型,因此将被忽略new。
function User(name, age) {
return {
name,
age,
}
};
let user = User("Tom", 23);
Run Code Online (Sandbox Code Playgroud)
在这里,工厂函数不带new。如果函数的参数及其返回的对象类型,则该函数完全负责直接或间接使用。在此示例中,它返回一个简单的[Object object],其中包含通过参数设置的一些属性。
轻松向调用者隐藏对象创建的实现复杂性。这对于浏览器中的本机代码功能特别有用。
工厂函数不必总是返回相同类型的对象,甚至可以null作为错误指示符返回。
在简单的情况下,工厂功能的结构和含义可能很简单。
返回的对象通常不会从工厂函数的prototype属性继承,而false从继承instanceOf factoryFunction。
无法使用extends关键字安全地扩展工厂函数,因为扩展对象将继承自工厂函数prototype属性,而不是继承自工厂函数prototype使用的构造函数的属性。
| 归档时间: |
|
| 查看次数: |
48474 次 |
| 最近记录: |