2

From an exercise at codeacademy:

function Penguin(name) {
    this.name = "Pingy";
    this.numLegs = 2;
}

// create your Emperor class here and make it inherit from Penguin
function Emperor (name){
    this.name = name;
}
Emperor.prototype = new Penguin();
var emp = new Emperor("Empy");

So I made Emperor inherit properties from Penguin, now I know that emp.numLegs will be 2.

After reading the comments I edited the question: As you can see I gave a name upon creating emp, and emp.name will indeed be the name of my new emp, which is "Empy". But what about the name inherited from the Penguin class constructor? Where does it go?

frrlod
  • 6,475
  • 8
  • 31
  • 37

4 Answers4

8

Codecademy is a great way for beginners to learn JavaScript. Learning any programming language takes practice, and Codecademy makes you practice.

Sometimes however you need to go beyond the scope of the practicals and learn some theory. Read the following answer. It explains prototypal inheritance in JavaScript really well: https://stackoverflow.com/a/8096017/783743

Now you have a Penguin constructor as follows:

function Penguin(name) {
    this.name = name;
    this.numLegs = 2;
}

Then you create an Emperor constructor which inherits from Penguin:

function Emperor(name) {
    this.name = name;
}

Emperor.prototype = new Penguin();

Notice that you're creating an instance of Penguin and assigning it to Emperor.prototype. Hence:

  1. Emperor.prototype.name = undefined - this is because you're not passing any name to the Penguin constructor. If I'd written Emperor.prototype = new Penguin("Empy") then Emperor.prototype.name would be "Empy".
  2. Emperor.prototype.numLegs = 2 - obviously.

Now when you create a new Emperor as follows you need to give the constructor a name (remember that you're inheriting undefined from Penguin as a name). Hence:

var emp = new Emperor("Empy");

That was the old school method.

Nowadays JavaScript programmers use Object.create and call to inherit from another constructor. Let me explain with an example:

function Emperor(name) {
    Penguin.call(this, name);
}

Emperor.prototype = Object.create(Penguin.prototype);
Emperor.prototype.constructor = Emperor;

This method has several advantages over the old school method:

  1. Instead of creating a new instance of Penguin using new Penguin() and passing no arguments to it, we inherit the prototype members of Penguin using Object.create(Penguin.prototype). This also prevent unnecessary initialization of the base constructor until the derived constructor is actually called.
  2. Instead of writing this.name = name in the derived constructor again we call the base constructor using Penguin.call(this, name) which does it for us. Such a pattern is called a mixin and is very useful if the base constructor needs to be initialized at runtime or needs to maintain its own state information.

Note that we have also added an additional statement Emperor.prototype.constructor = Emperor. This is because prototype is a very special property which exists on all functions. It points to an object which has a very special constructor property which points to the function itself. By setting Emperor.prototype to something else we lose this property. Hence we set it again.

The net effect in both these examples is the same. However for more complex code it's much better to use the new method. Happy learning JavaScript.

Community
  • 1
  • 1
Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
  • There is actually an important difference in behavior between the two methods. In the old method, all `Emperor` instances share the reference to `numLegs`, so changing one will affect everyone. (In other words, all emperor penguins have the same number of legs.) With the new version, the value is copied into the instance instead of referring to a copy in the prototype. – Hjulle Oct 21 '16 at 15:33
  • @Hjulle Indeed. However, you shouldn't be doing that. – Aadit M Shah Oct 22 '16 at 04:51
  • I'd argue that it's nothing inherently wrong with modifying an inherited property. We'd get the same problem even if the parent had a setter. What you definitely shouldn't do is putting anything but methods in the prototype (which the first method does). That is just a recipe for disaster. At least if you do not make sure that the properties are not modifiable. (Or actually want a global mutable state that is shared between instances, but it's a pretty obscure/confusing way to do it.) – Hjulle Oct 22 '16 at 05:18
5

I assume you are wondering why you have to provide name as parameter to Emperor and this.name = name; inside the constructor.

Prototype inheritance in JavaScript is very simple, there is no hidden magic going on. The only connection you provided between Emperor and Penguin is in this line:

Emperor.prototype = new Penguin();

Emperor.prototype is now an instance of Penguin, but it could be any object for that matter. The function Emperor does not know anything about the function Penguin, so it does not magically call Penguin.

When you call a function (Func) with new, all it does is creating a new empty object, whose prototype is the functions prototype property (Func.prototype). This object will become the this value inside the constructor and will be returned implicitly if no other object is returned.

But what about the name inherited from the Penguin class constructor? Where does it go?

It's a property of Emperor.prototype and is shadowed by each Emperor instance's name property.

Have a look at my answer here where I created some nice ASCII charts explaining the relation between instances and their prototypes.


Note: Emperor.prototype = new Penguin(); is actually not a good way of establishing the inheritance. What happens if Penguin requires an argument? What would pass?

At this moment you actually don't want to create a new instance of Penguin, you only want to hook up Penguin's prototype into the prototype chain. You can do this easily with Object.create:

Emperor.prototype = Object.create(Penguin.prototype);
Emperor.prototype.constructor = Emperor;

But then new Emperor objects wouldn't have a numLegs property anymore. That's why you have to call the Penguin on each new Emperor instance, just like do in other languages with super():

function Emperor (name){
    Penguin.call(this, name);
} 
Community
  • 1
  • 1
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
1

When calling new Penguin(), this method will create an object and return the prototype of that object.

When doing prototype inhertance, the only thing you do is creating a new Penguin object first and put those values inside the prototype of Emperor. Now, when you are creating a new Emperor object, the prototype is already set to the values of Penguin. To override those, you need to set these again in a new constructor.

Inhertiance in JavaScript is very different from other languages.

A code example:

function Penguin(name) {
    this.name = name;
    this.legs = 2;
}

function Emperor(name) {
    this.name = name;
}
Emperor.prototype = new Penguin();
/**
 * The prototype of Emperor now contains:
 * 
       {
           "name": undefined,
           "legs": 2
       }
 * 
 * Because you didn't add an argument to the constructor of `Penguin`
 */

var e = new Emperor('foo');
// The name property will get overriden with 'foo'
Wouter J
  • 41,455
  • 15
  • 107
  • 112
1

The Penguin object also when created requires a name to be passes into the name parameter. So when you create a new object of type Penguin you have to pass the name. Similarly since Emperor inherits from the Penguin object you have to pass the value in the constructor so that if you wish to do a different processing than that which happens in the base class Penguin you can define it so in the sub class constructor. But Emperor can be an object object of any type since it has no information on definition of Penguin.

If in the definition of the class 'Emperor' you remove the line

 this.name = name;

Then you can pass any variable in the constructor and still get the value "Pingy" as defined in the super constructor

and if you add the line

 Penguin.call(this, name) ;// As to call super class instance

Then you get the value passed properly onto the new object with the numLegs as 2 also

Ajo Koshy
  • 1,205
  • 2
  • 21
  • 33