简体   繁体   中英

How to implement super mechanism in javascript through prototype?

I am struggling to understand the constructor invocation pattern in Javascript. I have a base object Mammal ( would it be incorrect to use the term class ? ) and an inherited object Cat . In the following code the object Cat correctly inherits from the Mammal object.

/* 
  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());

What I don't like in this pattern is how the constructor of the base object is used. The object Cat does not reuse correctly the constructor of the base class Mammal . I would like to have a more flexibility. Each time a Cat object is created, the constructor of the Mammal object is invoked with no arguments. I would like to use a mechanism similar to the "super" keyword in Java, so that when the constructor of Cat is called with name as parameter, also the constructor of Mammal is called with name as parameter.

I tried to implement the Cat constructor as follows :

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

This does not work as expected. this.prototype is undefined. why? Why this approach is completely wrong? does this point to the newly Cat object?

I know, there are different ways to implement inheritance in javaScript, but I am wondering if there is a way to implement the super mechanism like in Java.

Thanks. :D

How about

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

Yes, I'm afraid that's not how you set up hierarchies. It's close, but there are a couple of key issues. (One of which — calling new Mammal() to create Cat.prototype — is a very, very, very frequent error you see in a lot of blog posts and such.)

Here's a simple example of doing it correctly:

// 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;  
};

If you're interested in doing inheritance hierarchies in JavaScript, you may find my Lineage script useful. You may or may not choose to use it, but it demonstrates how to set things up, a way to do calls to the parent's version of methods ("supercalls"), etc. In particular, this documentation page comparing using Lineage to not using it shows how to do this without any helper script. But there's a reason I wrote a helper script to do it. :-)

this.prototype is undefined, because no one defined it.

Cat is a function. As such, it has a property prototype . That's mandated by the ECMAScript standard.

this is an object that is not a function. As such, the ECMAScript standard does not mandate that it has a prototype property.

If this is a Cat (ie an object that was or is created using new Cat ), then it has, for the sake of specification , an internal [[Prototype]] property which is a Mamal . But this mamal is not accessible directly (as implied by the word internal ). When you say var maru = new Cat() , then maru.[[Prototype]] is linked to Cat.prototype . That's how maru knows about future methods of mamals.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM