好的,所以我将尝试在这里回答我自己的问题,其中包含我收到的信息以及我在提出问题后在互联网上收集的其他内容。
TL;DR:
它们都很有用,并且可以实现几乎相同的目标。构造函数可以访问他们的原型,这非常有用,因为这意味着它们在使用构造函数创建的所有实例中都具有“全局”值。它既有用又有潜在危险。有用,因为 Constructor 的所有实例都可以访问相同的原型属性,从而避免重复。很危险,因为您可以覆盖构造函数属性或给实例一个同名的属性 - 使得访问原型值变得更加困难。
在调用构造函数时忘记new 关键字存在一定的危险,但可以通过在构造函数中添加"use strict"; 轻松解决,如果忘记new 关键字则会抛出错误。
如果您想避免原型及其功能/危险,您可以使用工厂
功能。
函数式方法真正有用的特性是你可以返回任何你喜欢的东西。而不是总是构造一个预定义对象的“子”。
我从这一切中学到的是,当你可以同时使用两者时,选择一个而不是另一个是愚蠢的。他们都有自己的长处和短处,人们需要记住,Douglas Crockford 只是一个人,而不是 JavaScript 之神。 (那就是 Brandon Eich,哈哈!)
@Domenic 在What difference is there in JavaScript between a constructor function, and function returning object which is invoked as a constructor? 上接受的答案
让我了解了这两种对象创建方法的异同。
构造函数
使用new 关键字在新对象和派生它的构造函数对象之间创建一个链接。构造函数是新对象的原型,新对象是原型对象的一个实例。
var Constructor = function () {
this.x = 0;
this.y = 0;
};
var A = new Constructor();
console.log(A instanceof Constructor ); // true
链接到原型对象意味着我们的新对象可以访问原型属性,而不必将它们存储在对象本身中。这不仅比在每个子对象上创建属性更节省内存,而且还带来了原型设计功能的额外好处。
向对象原型添加属性或方法很简单:
Constructor.prototype.color = 'yellow';
现在,使用 Constructor 对象创建的每个对象都可以访问.color 属性,而无需将其存储在自身内部。
var A = new Constructor();
console.log(A.color); // yellow
console.log(A.hasOwnProperty('color')); // false
由于 JavaScript 中的对象是动态的,这意味着您可以“追溯”向原型添加新属性,并且在更改之前创建的对象仍将“继承”新属性。
var A = new Constructor();
Constructor.prototype.food = 'bacon';
console.log(A.food); // bacon;
Crockford 可能反对构造函数模式的一个原因是避免覆盖原型属性或意外覆盖子对象内原型的命名空间。
Constructor.prototype.number = 5;
A.calculate = function () {
return A.number * 5;
}
console.log(A.calculate()); // 25
Constructor.prototype.number = 'fishsticks';
console.log(A.calculate()); // NaN
据我所知,在创建后添加属性也会使代码在 V8 引擎中运行得更慢,因为对象不再共享相同的 “隐藏类” 但我没有足够的知识来了解进入那个。 Breaking the JavaScript Speed Limit with V8
原型仍然可以访问。通过现在已弃用的 .__proto__. 或新的Object.getPrototypeOf() 方法。
console.log(Object.getPrototypeOf(A.color)); // yellow
Crockford 反对使用构造函数的另一个原因是您可能忘记输入new。如果您忘记在构造函数前面写new,它将运行构造函数而不是创建新对象。
var A = Constructor();
console.log(A); // undefined
这很容易通过在你的函数中添加严格的类型来解决,如果你忘记了new 关键字,将会抛出一个错误。
var Constructor = function () {
"use strict";
this.x = 0;
this.y = 0;
}
var A = Constructor();
console.log(A);
// Uncaught TypeError: Cannot set property 'x' of undefined
工厂函数
我发现这很简单。如果您不想处理 new 关键字以及 Constructor 函数的一些“危险”,您可以使用这种方法创建不使用其原型的对象。
function factory () {
var obj = {
x: 0,
y: 0
}
return obj;
}
var A = factory(); // {x: 0, y: 0}
当您想要对数据做一些事情而不只是创建一个对象时,这会非常方便。
function factory () {
if ( new Date().getHours() < 8 ) {
return "can't create object. Need Coffe!"
};
var obj = {
x: 0,
y: 0
}
return obj;
}
var A = factory(); // Before 8 am: "can't create object. Need Coffe!"
var A = factory(); // After 8 am: {x: 0, y: 0};
这样做你会失去原型的力量/危险。因为对象没有绑定到一个。
factory.prototype.foo = "bar";
A = factory();
console.log(A.foo); // undefined
这意味着你不能使用它。但这也意味着你不能把事情搞砸。
总结。
参见 TL;DR
我在搜索和写这篇文章时学到了很多东西,希望其他人也能学到一两件事。
参考资料:
What difference is there in JavaScript between a constructor function, and function returning object which is invoked as a constructor?
Constructor function vs Factory functions
It’s time to start using JavaScript strict mode