过去在JavaScript中创建“类”时,我这样做了:
function Dog(name){
this.name=name;
this.sound = function(){
return "Wuf";
};
}
然而,我只是看到有人这样做:
var Dog = (function () {
function Dog(name) {
this.name = name;
}
Dog.prototype.sound = function () {
return "Wuf";
};
return Dog;
})();
你可以两种方式做到,还是我做错了?在那种情况下,为什么?那两者在我们最终的结果方面究竟有什么区别?在这两种情况下,我们都可以通过以下方式创建对象:
var fido = new Dog("Fido");
fido.sound();
我希望有人能够启发我。
答案 0 :(得分:15)
你和他们的方式有两个重要的区别。
(function() { ... })();
).prototype
属性而非this.
方法。在自调用函数中包装事物,然后将结果(如return
语句中定义的那样分配给变量称为module pattern。它是确保范围更广泛的常见模式控制。
使用Dog.prototype.sound = function() {}
首选至this.sound = function()
。不同之处在于Dog.prototype.sound
为具有Dog
构造函数的所有对象定义一次,并且this.sound = function() {}
再次为每个创建的Dog对象定义 。
经验法则是:对象(通常是其属性)的个体将在this
上定义,而对同一类型的所有对象(通常是函数)共享的东西是在原型上定义。
答案 1 :(得分:4)
使用您的代码,您可以为正在创建的每个新sound
实例创建新功能Dog
。 Javascript' s prototype
通过仅创建所有对象实例共享的单个函数来避免这种情况;基本上是经典继承。
在第二个代码中,您显示的是另外包含在IIFE中,在这种情况下,它并没有做太多。
答案 2 :(得分:3)
第一个是创建构造函数的传统方法。第二个是立即调用的函数表达式,它返回一个构造函数。此方法允许您将变量保留在模块中,而不会泄漏到全局范围内,这可能是一个问题。
两者在我们最终的结果方面究竟有什么不同?
正如您所见,他们都有相同的结果。其他人已经谈过prototype
所以我不会在这里提到它。
答案 3 :(得分:2)
第二个是优选的,因为它利用了Javascript的原型继承机制。
<强>原型强>
Javascript继承是一个混乱的原因,但它实际上相当简单:每个对象都有一个原型,当我们尝试访问不在原始对象上的属性时,我们将检查该对象。原型本身就有原型;在一个简单的例子中,如Dog
,这可能是Object.prototype
。
在您的两个示例中,由于new
operator works的方式,我们最终会得到一个如下所示的原型链:fido->Dog.prototype->Object.prototype
。所以,如果我们试图在Fido上寻找name
属性,我们就会在对象上找到它。另一方面,如果我们查找hasOwnProperty
属性,我们将无法在Fido上找到它,无法在Dog.prototype
找到它,然后到达Object.prototype
,我们在那里我会找到的。
在sound
的情况下,您的示例在两个不同的位置定义它:在第一种情况下,fido
和我们创建的每个其他狗都将拥有自己的函数副本。在第二种情况下,Dog.prototype
将具有该函数的单个副本,当调用该方法时,各个狗将访问该副本。这避免了在存储sound
函数的重复项时浪费资源。
这也意味着我们可以扩展原型链;也许我们想要一个继承Corgi
sound
函数的Dog
类。在第二种情况下,我们可以简单地确保Dog.prototype
在Corgi.prototype
的原型链中;首先,我们需要创建一个真正的Dog并将其放入原型链中。