Does doing the Terrarium.call(this, plan)
pass the data to the Terrarium
constructor as if you had called Terrarium(plan)
within the context of the inheritence?
Yes. Function#call
calls the function with a given this
value and arguments provided individually. Meaning we specify the this
inside the called function to be whatever object we want. Terrarium.call(this, plan)
passes our current this
(which will be an instance of LifeLikeTerrarium
) to the call to Terrarium
. If Terrarium
assigns something or modifies this
inside itslef, the this
it modifies will be what we passed to it using call
, here the current instance of LifeLikeTerrarium
. The rest of the parameters to call
are all passed along to the called function as individual paramaters.
Demonstration:
function foo(a, b) {
this.sum = a + b;
}
var obj = {};
foo.call(obj, 5, 7); // obj will be the 'this' used inside foo.
// 5 and 7 will be the values of foo's arguments a and b, respectively
console.log(obj); // et voilà
What is the purpose of calling clone(object)
? How is this different from saying LifeLikeTerrarium.prototype = Terrarium.prototype
?
We don't want to do LifeLikeTerrarium.prototype = Terrarium.prototype
. Ever. Because LifeLikeTerrarium
is most likely going to have methods of its own that we don't want added to Terrarium
as well. Imagine we add a method to LifeLikeTerrarium
's prototype called foo
. foo
will also be available to Terrarium
. Even worse, foo
could replace Terrarium
's own method foo
, if it happens to have one:
function Terrarium() { }
Terrarium.prototype.sayHi = function() {
console.log("Hi! I'm Terrarium");
}
function LifeLikeTerrarium() { }
LifeLikeTerrarium.prototype = Terrarium.prototype; // now LifeLikeTerrarium.prototype and Terrarium.prototype are both referencing the same object, changes to one are reflected on the other
LifeLikeTerrarium.prototype.sayHi = function() { // Terrarium.prototype.sayHi is gone for ever. May it rest in peace.
console.log("Hi! I'm LifeLikeTerrarium");
}
var terrarium = new Terrarium();
terrarium.sayHi(); // wrong wrong wrong
As you can see, this is very bad. Depending on what the method does, it could even throw an error and break the execution (if for example the new method uses properties that aren't in the original object Terrarium
):
function Terrarium() { }
Terrarium.prototype.sayHi = function() {
console.log("Hi! I'm an abstract Terrarium that doesn't have a name");
}
function LifeLikeTerrarium(name) {
this.name = name;
}
LifeLikeTerrarium.prototype = Terrarium.prototype;
LifeLikeTerrarium.prototype.sayHi = function() {
console.log("Hi! I'm " + this.name.toUpperCase()); // b.. b.. but Terrarium doen't have a property called name. Let's see what will happen. Maybe it'll work
}
var terrarium = new Terrarium();
terrarium.sayHi(); // Surprise! You got mail, I mean an error
In addition to this, we, at the very least, will be poluting the original object with methods that it won't even need or use:
function Terrarium() { }
function LifeLikeTerrarium() { }
LifeLikeTerrarium.prototype = Terrarium.prototype;
LifeLikeTerrarium.prototype.sayHi = function() { // now Terrarium has a method called sayHi although it originally didn't have one.
console.log("Hi!");
}
var terrarium = new Terrarium();
terrarium.sayHi(); // Want proof? Here you go.
How do we get arround this? The answer is: We don't assign Terrarium.prototype
to LifeLikeTerrarium.prototype
so that both prototypes reference the same object. No, we assign to LifeLikeTerrarium.prototype
a plain new object that has its prototype set to Terrarium.prototype
. The new plain object (which is the result of new OneShotConstructor()
) will act as LifeLikeTerrarium
's own prototype. That plain object will have its prototype set to Terrarium.prototype
which causes Terrarium
's methods to be available to LifeLikeTerrarium
, a phenomenon that goes by the name of "Inheritance". Here is a diagram of the difference between the two ways:
LifeLikeTerrarium.prototype = Terrarium.prototype
:
Terrarium.prototype ---------------------> {
| ...
| ...
| }
|
LifeLikeTerrarium.prototype ---/
LifeLikeTerrarium.prototype = clone(Terrarium.prototype)
:
Terrarium.prototype ---------------------> {
| ...
| ...
| }
|
\------------------------------\
|
LifeLikeTerrarium.prototype -------------> { |
... |
... |
prototype: ---/
}
As you can see, in the first diagram, both Terrarium.prototype
and LifeLikeTerrarium.prototype
are referencing the same object. Whereas in the second diagram each is referencing it's own object: Terrarium.prototype
is referencing its own object which is defined somewhere in your code and LifeLikeTerrarium.prototype
is referencing an instance of OneShotConstructor
(which is an empty object because the constructor is an empty one). Another benifit is that LifeLikeTerrarium.prototype
's prototype is referencing Terrarium.prototype
(made possible because of OneShotConstructor.prototype = object;
). This creates a nice prototype chain that lets methods of Terrarium
be available to LifeLikeTerrarium
if the latter doesn't want to have its own, and if it does (want to have its own), that will be totally fine because they won't replace Terrarium
's, they'll just shadow them. And this is the essence of prototype inheritence.
What is the purpose of assigning LifeLikeTerrarium
's constructor to itself?
This already has an answer here: Why is it necessary to set the prototype constructor?