1

为什么下面的“Cat.prototype = new Mammal()”行在 Cat() 函数内部不起作用,但在 Cat() 函数外部起作用?

function Mammal() {
  Mammal.prototype.hasHair = function() { return true; }
}

alert( new Mammal().hasHair() ); // dispays true here

function Cat() {
  Cat.prototype = new Mammal();
}

try {
  new Cat().hasHair(); // doesn't work. why?
} catch ( err ) {
  alert('error'); // displays error here
}

Cat.prototype = new Mammal(); // same line as inside Cat() function

alert( new Cat().hasHair() ); // now it works

我尝试了几种不同的 javascript 实现,所以我怀疑它是一种实现特性。我想知道这主要是出于好奇,但也只是为了组织,我想在 Cat 的函数中定义 Cats,而不是到处传播。

4

6 回答 6

3

在您执行的那一刻new Cat,将创建一个继承自的空对象Cat.prototype。是什么Cat.prototype?这是一个空对象,因为您还没有修改它。

在构造函数内部,您正在分配Cat.prototype = new Mammal();. 的下一个实例Cat将从该Mammal实例继承。但是,创建第二个实例也会创建另一个Mammal实例,第三个实例从该Cat实例继承,依此类推。

你最终得到这个继承链(catX是的第x个实例Cat并且mammalX是的第x个实例Mammal):

cat0 -> Object
cat1 -> mammal0 -> Mammal.prototype
cat2 -> mammal1 -> Mammal.prototype
...

每个Cat实例都有自己的原型,这在某种程度上与原型的用途相反(共享公共代码)。它可能看起来没什么大不了的,但它有几个后果,包括:

  • 增加内存使用:(几乎)每个Cat实例都有一个对应的Mammal实例,所以你有2N对象而不是N + 1.
  • 继承测试中断:instanceof通过比较原型来工作,并且由于原型cat0不同Cat.prototype(在创建多个实例之后),cat instanceof Cat将产生false

您真正想要的继承链是:

cat0 -> Mammal.prototype
cat1 -> Mammal.prototype
cat2 -> Mammal.prototype
...

永远不要在构造函数中为原型赋值(或覆盖原型)。构造函数应该只设置实例特定的属性(在某些情况下,您可能想要更改静态属性,但这些不太常见)。

在构造函数之外设置原型:

function Mammal() {}       
Mammal.prototype.hasHair = function() { return true; }

function Cat() {
    Mammal.apply(this, arguments);
}
Cat.prototype = Object.create(Mammal.prototype);
Cat.prototype.constructor = Cat;

Object.create创建一个继承自作为参数传递的对象的对象。见MDN

于 2012-10-29T00:53:48.747 回答
2

因为成员在构造函数执行之前prototype被传递给对象实例。

http://jsfiddle.net/vSDbB/

function Mammal() {
  Mammal.prototype.hasHair = function() { return true; }
}

function Cat() {
    Cat.prototype = new Mammal();
}

new Cat();
alert(new Cat().hasHair());

所以上面的代码可以工作,但是如果你注释了第一new Cat();行它就不会因为在这new Cat().hasHair()条线上 Cat's prototype doesn't have hasHair()method

于 2012-10-29T00:40:20.283 回答
1

我编辑了您的代码,以便它可以实现您的目标。以下代码创建一个基类,然后创建一个与您尝试执行的样式相同的子类。

function Mammal() {
  this.hasHair = function() { return true; }
}

alert( new Mammal().hasHair() ); // dispays true here

function Cat() {
   Mammal.call(this);

}

try {
  alert( new Cat().hasHair() ); // dispays true here
} catch ( err ) {
  alert('error'); // should not fire
}

​</p>

于 2012-10-29T00:53:16.473 回答
1

我有点困惑。你实际上期待什么?

您正在将一个函数分配到原型链Cat,但是您是在调用构造函数时这样做的。如果您将其称为 like new Cat().hasHair(),则调用对象根本不知道该函数。

您需要.prototype在实际调用构造函数之前扩展 。

于 2012-10-29T00:40:35.573 回答
1

因为您在每次实例化时都覆盖了原型。Javascript 并没有真正的继承。

当你这样做时

function Mammal() {
    Mammal.prototype.hasHair = function() { return true; }
}
new Mammal().hasHair();

它的意思是

  1. 用作Mammal构造函数并实例化new Mammal类型对象
  2. 覆盖Mammal每次执行此操作的原型

但如果你这样做:

function Cat() {
}
Cat.prototype = new Mammal();
new Cat().hasHair();

它的意思是

  1. Cat构造函数的原型为new Mammal type object
  2. 通过“类”实例化一个new Cat type object“继承”方法的方法hasHair()Mammal

希望这可以帮助。

于 2012-10-29T01:03:40.020 回答
0

函数的原型(充当类)是创建新对象时复制的对象。您不应该在构造函数的原型上设置属性。

要么:

function Mammal() {
}
Mammal.prototype.hasHair = function(){ return true };

或这个:

function Mammal() {
    this.hasHair = function(){ return true }
}
于 2012-10-29T00:47:06.040 回答