javascript继承陌生感

时间:2012-10-29 00:36:07

标签: javascript inheritance

为什么下面的“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,而不是遍布各处。

6 个答案:

答案 0 :(得分:2)

执行new Cat时,会创建一个继承自Cat.prototype的空对象。什么是Cat.prototype?它是一个空对象,因为你尚未修改它。

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

您最终拥有此继承链(catXCat的第x个实例,而mammalXMammal的第x个实例):

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

每个Cat实例都有自己的原型,这与原型用于(共享公共代码)有些相反。它可能看起来不是什么大问题,但它有几个后果,包括:

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

您真正想要的继承链是:

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; 创建一个继承自您作为参数传递的对象的对象。请参阅MDN

答案 1 :(得分:1)

因为在构造函数执行之前将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的原型没有hasHair()方法

答案 2 :(得分:0)

我有点困惑。你到底想要什么?

您正在从Cat函数分配到原型链中,但您需要调用构造函数。如果您将其称为new Cat().hasHair(),则调用对象根本不了解该功能。

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

答案 3 :(得分:0)

函数的原型(作为一个类)是新对象在创建时复制的对象。您不应该在构造函数中设置原型的属性。

要么:

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

或者这个:

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

答案 4 :(得分:0)

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

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
}

答案 5 :(得分:0)

因为你在每个实例化时都要覆盖原型。 Javascript实际上没有继承。

当你做

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

意味着

  1. 使用Mammal作为构造函数并实例化new Mammal类型对象
  2. 每次执行此操作时都会覆盖Mammal的原型
  3. 但如果你这样做:

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

    意味着

    1. Cat构造函数的原型为new Mammal type object
    2. 通过new Cat type object'类'
    3. 实例化{继承'hasHair()方法的Mammal 希望这会有所帮助。