如何通过原型在javascript中实现超级机制?

时间:2013-12-29 12:15:04

标签: javascript inheritance constructor prototype

我正在努力理解Javascript中的构造函数调用模式。 我有一个基础对象Mammal(使用术语 class ?)和继承的对象Cat是不正确的。在以下代码中,对象Cat正确地从Mammal对象继承。

/* 
  Mammal base Object 
*/

var Mammal = function(name) {
  this.name = name;
}  

Mammal.prototype.get_name = function() {
  return this.name;    
} 

Mammal.prototype.says = function () {
  return this.saying || ''; 
} 

/*
  Cat object 
*/

var Cat = function (name) {
  this.saying = "Meow";  
  this.name = name; 
}

Cat.prototype.purr = function (number) {

  var i =0, s=''; 

  for ( i=0; i<number; i++)
     if (s)   
       s +='-'; 
     s+='r'; 

  return s;  
}

Cat.prototype = new Mammal(); 

console.log("Pseudo classical inheritance approach"); 
var mammal = new Mammal(" I am a mammal"); 
console.log("Who are you ? " + mammal.get_name()); 
console.log("What are you saying? " + mammal.says()); 

var cat = new Cat('I am a cat'); 
console.log("Who are you ? " + cat.get_name()); 
console.log("What are you saying? " + cat.says());

我在这种模式中不喜欢的是如何使用基础对象的构造函数。对象Cat未正确重用基类Mammal的构造函数。我想有更多的灵活性。每次创建Cat对象时,都会调用Mammal对象的构造函数而不带参数。我想在Java中使用类似于“super”关键字的机制,以便在使用Cat作为参数调用name的构造函数时,也会调用Mammal的构造函数以name作为参数。

我尝试按如下方式实现Cat构造函数:

  var Cat = function (name) {
      this.saying = "Meow";  
      // Super (name);
      this.prototype = new Mammal(name); 
    }

这不能按预期工作。 this.prototype未定义。为什么?为什么这种方法完全错误? this是否指向新Cat对象?

我知道,在javaScript中有不同的方法来实现继承,但我想知道是否有一种方法可以像Java一样实现超级机制。

感谢。 :D

3 个答案:

答案 0 :(得分:2)

怎么样

var Cat = function (name) {
  this.saying = "Meow";  
  // Super (name);
  Mammal.call( this, name );
}

答案 1 :(得分:1)

是的,我担心这不是你设置层次结构的方式。它很接近,但有一些关键问题。 (其中一个 - 调用new Mammal()来创建Cat.prototype - 是一个非常,非常非常频繁的错误,你会在很多博客文章等中看到。)

以下是正确执行此操作的简单示例:

// A function to set up the link between a child and parent
function derive(Child, Parent) {
    // `ctor` is a temporary function we use so we can get an object
    // backed by `Parent.prototype` but without calling `Parent`.
    function ctor() { }

    // Borrow the prototype
    ctor.prototype = Parent.prototype;

    // Create an object backed by `Parent.prototype` and use it as
    // `Child`'s prototype
    Child.prototype = new ctor();

    // Some housekeeping to make the prototype look like the ones
    // the JavaScript engine creates normally.
    Child.prototype.constructor = Child;

    // Note: If we can rely on ES5 features, we could use
    // `Object.create` instead of the `ctor` function.
}

// The parent constructor    
var Mammal = function(name) {
    this.name = name;
};

// Some stuff for its prototype
Mammal.prototype.get_name = function() {
  return this.name;    
};

Mammal.prototype.says = function () {
  return this.saying || ''; 
};

// The child constructor    
var Cat = function(name) {
  Mammal.call(this, name);
  this.saying = "Meow";  
};

// Hook it up to the parent
derive(Cat, Mammal);

// Add some things to its prototype
Cat.prototype.purr = function (number) {

  var i =0, s=''; 

  for ( i=0; i<number; i++)
     if (s)   
       s +='-'; 
     s+='r'; 

  return s;  
};

如果您对使用JavaScript进行继承层次结构感兴趣,可能会发现我的Lineage script很有用。您可能会也可能不会选择使用它,但它演示了如何设置,调用父版本的方法(“超级调用”)等等。特别是this documentation page使用{{进行比较1}}不使用它显示如何在没有任何帮助脚本的情况下执行此操作。但是有一个原因我写了一个帮助脚本来做它。 : - )

答案 2 :(得分:1)

this.prototype未定义,因为没有人定义它。

Cat是一个功能。因此,它具有属性prototype。这是ECMAScript标准规定的。

this是一个不是函数的对象。因此,ECMAScript标准并未强制要求它具有prototype属性。

如果thisCat(即使用new Cat创建或创建的对象),则为了规范,内部[[Prototype]]属性,Mamal。但是这个mamal不能直接访问(正如 internal 这个词所暗示的那样)。当您说var maru = new Cat()时,maru.[[Prototype]]Cat.prototype相关联。这就是maru对未来mamals方法的了解。