Why wouldn't I use Child.prototype = Parent.Prototype rather than Child.prototype = new Parent(); for Javascript inheritance?
If you do
Spaceship.prototype = GameObject.prototype;
Then they both refer to the same object, so you might as well have everything in GameObject
, if you add something to Spaceship.prototype
, it will be added to GameObject.prototype
as well. You can easily test it by adding something to Spaceship.prototype
after the assignment. For example, in your case you can see that GameObject.prototype.constructor
is actually Spaceship
.
As for
Spaceship.prototype = new GameObject();
This invokes the constructor which might have undesired side effects, you rather want to use:
Spaceship.prototype = Object.create(GameObject.prototype);
Where the used Object.create
functionality here comes down to:
Object.create = function( proto ) {
function f(){}
f.prototype = proto;
return new f;
};
Modern browsers already have the function though.
Proper way to inherit an object constructor's prototype?
Option 1 is definitely the better approach, because it creates a new object that inherits from but is not the same as User.prototype
.
Option 2 means that any changes to Admin.prototype
will be exactly reflected on User.prototype
, which is not desirable; with that approach, they're the exact same object.
Demo:
function User(email, name) {
this.email = email;
this.name = name;
this.online = false;
}
User.prototype.login = function() {
this.online = true;
console.log(this.email, 'has logged in');
}
function Admin(...args) {
User.apply(this, args);
this.role = 'super admin';
}
Admin.prototype = User.prototype; //option 2
Admin.prototype.getAdminInfo = function() {
console.log('getting admin info');
}
const user = new User('useremail');
user.getAdminInfo();
Equality between prototypes in javascript
This part of that code is wrong:
UrgentTask.prototype = Task.prototype;
That just makes the two classes have the exact same prototype object. Change one and the other will be changed too which is not how inheritance works.
The usual way to derive from a class like this is:
UrgentTask.prototype = Object.create(Task.prototype);
which creates a new, separate prototype object incorporating the parent properties into the new object.
See a further example here on MDN.
Why do changing in UrgentTask.prototype affects Task.prototype, I mean shouldn't the equality goes one way ?
There is no one-way equality in Javascript so I'm not sure what you mean by that. When you assign an object as in x = y
where x
and y
are objects, you end up with a situation where both variables x
and y
now point at the exact same object. The object isn't copied into a separate object. Both variables point at the same object.
For more description of how objects are assigned from one variable to another, see these references:
reading / assigning reference types in JavaScript
Is global variable assignment atomic on NodeJS?
Here's a very simple example in a runnable snippet:
let x = {greeting: "hello"};let y = x;
console.log("x", JSON.stringify(x));console.log("y", JSON.stringify(y));console.log('x.greeting = "goodbye"');
x.greeting = "goodbye";
console.log("x", JSON.stringify(x));console.log("y", JSON.stringify(y));console.log("x === y is", x === y);
Why do we use a temporary box in prototype inheritance?
Well. The main difference in both the function are in the lines
Temp.prototype = Parent.prototype;
and Child.prototype = new Parent();
The first extend
function shows prototypal inheritance solely. The Child
wouldn't inherit any property from the Parent
, which isn't there in its' prototype, as you can see, you are not calling the parent's constructor
anywhere.
I have created a fiddle to explain this here.
In the second extend function, as you are calling the Parent's constructor
Child.prototype = new Parent()
, all the properties of the Parent
, which are there in Parent's prototype alongwith all those which aren't will be inherited by the Child
; i.e it will all go into the Child's prototype
.
I have created a fiddle here to explain this as well.
Can modifying the prototype on a child affect the parent's prototype?
After Child.prototype = Parent.prototype
the same object is bound to both constructors' prototype property (and is fairly silly, IMOHO). Thus modifying the object known as Child.prototype
also modifies Parent.prototype
which "affects all" as claimed because both expressions evaluate to the same object.
Consider if later on;
Child.prototype.hello = "Hello world"
// Noting that
Child.prototype === Parent.prototype // -> true, as per initial condition
Then;
var p = new Parent()
p.hello // -> "Hello world", showing the "affecting" behavior
So it can be see that modifying Child.prototype affected the [prototype] of instances of Parent as well - because the same object was modified.
However, the issue isn't being reproduced because one cannot "assign the prototype of an instance" in such a manner. That is, the [prototype] chain is only set based of the constructor's prototype property (or via Object.create), at time of creating a new instance. Assigning to the prototype property of an instance is .. just assigning a normal property.
var c = new Child()
c.prototype = "Hello world"
typeof c.say_a // -> "function", didn't assign [prototype]
c.prototype // -> "Hello world"
c.__proto__ === Child.prototype // -> true, in browsers supporting __proto__
JavaScript prototype inheritance
In this particular case you can replace the lines for a simple reason:
function Dog(color, age) {
this.color = color;
Animal.call(this, age); // This line
}
Here, you're calling Animal's constructor with call
providing it this
thus establishing Animal's context manually. And so this
inside function Animal
will always refer to the Dog
's object. That's why a new
won't make a difference at all.
Strictly speaking, you're not actually doing proper prototypal inheritance. You can confirm that by doing:
dog_b.hasOwnProperty('age'); // => true
This is actually called constructor stealing
where Dog
stealing Animal
's constructor. The walk
method though is on Animal
's ctor.
Which means the age
property is on Dog
itself and not on Animal
. Why is that? Again, because you're establishing Animal
's constructor context manually to be that of the Dog
.
Edit:
In Javascript there are various ways of describing and attaining inheritance, and since this is not a place for writing a book I'll be quick.
What you're doing is called Parasitic Combination Inheritance
(you don't have to remember the term). It's not wrong, it's simply another way of obtaining inheritance (which is prototypal only for functions that are on Animal.prototype
). If it's working for you I'd say take it. If you really really want Animal
's age
to be on Animal
, then you'll face problems when using constructors to obtain inheritance.
One way of doing this would be:
function inherit(o){
function F(){}
F.prototype = o;
return new F();
}
var foo = {
name: 'some'
}
var bar = inherit(foo); // bar.name refers to the object on foo
As you can see, prototypal inheritance is usually used for object literals (think angular's scope object).
javascript constructor.prototype inheriting another constructor.prototype
It's because if you add something to B.prototype you also will add to A.prototype
But why I just can`t call B.prototype = A.prototype since
Object.create(A.prototype) it is eqaul to A.prototype?
If you do B.prototype = A.prototype
then both prototypes will point to the same reference. If you do B.prototype = Object.create(A.prototype)
then B.prototype
will take only the values from A.prototype
, the reference won't be the same.
So,
B.prototype = A.prototype
B.prototype === A.prototype
// OUTPUT: true;
B.prototype = Object.create(A.prototype)
B.prototype === A.prototype
// OUTPUT: false;
EDIT
You can use the generated typescript code example to extend without Object.create
:
// b -> base class
// d -> new class
var __extends = function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};
For inheritance with JavaScript, is it better to reference or copy the parent's prototype?
child.prototype = parent.prototype;
is incorrect, for the reason you've detailed in your question.
Using _.extend
isn't want you want, either. Consider that a change in an "inherited" parent prototype property will not cause a change in the child:
// perform extension
_.extend(child.prototype, parent.prototype);
// parent later gains new property after extension
parent.prototype.parentProp = function() { alert("new"); };
(new parent()).parentProp(); // alerts "new" --> desirable
(new child()).parentProp(); // error; doesn't exist --> undesirable
You probably want child.prototype = Object.create(parent.prototype);
This says: "Store a new object in child.prototype
. This new object uses the parent.prototype
object as its own prototype." This causes real inheritance to happen, because when a child
instance wants a property, it first looks in its own properties, then in its immediate prototype (child.prototype
), and then in its prototype's prototype (parent.prototype
).
Prototype chains
When using
child.prototype = parent.prototype
, the prototype chains for the instances look like:{ child instance } ==> { only prototype }
{ parent instance } ==> { only prototype }where
only prototype
is the shared object referred to by bothchild.prototype
andparent.prototype
.When using
_.extend(child.prototype, parent.prototype)
, the child prototype and parent prototypes are different, but there's no direct inheritance. Changing the parent prototype doesn't change the child at all, since we merely copied the parent prototype's properties into the child prototype at one single point in time.{ child instance } ==> { child.prototype }
{ parent instance } ==> { parent.prototype }With
child.prototype = Object.create(parent.prototype);
you actually have inheritance happening from parent to child prototype:{ child instance } ==> { child.prototype } ==> { parent.prototype }
{ parent instance } ==> { parent.prototype }
Why can't I assign Parent.prototype to Son.prototype
There is no "foo" property on the "Parent" prototype object. The Parent()
constructor puts a "foo" property on each instance constructed.
So you are successfully sharing the same prototype object between the two constructors, but that doesn't achieve what your code expects it to achieve.
If your code had explicitly added a "foo" property to the "Parent" prototype like this:
function Parent() {}
Parent.prototype.foo = "this is foo";
Then this would work:
function Son() {}
Son.prototype = Parent.prototype;
alert(new Son().foo); // "this is foo"
Now, sharing prototype objects is certainly something you can do, but it's kind-of unusual.
Related Topics
How to Check If a Checkbox Is Checked
How to Determine User's Locale Within Browser
How to Wait for Set of Asynchronous Callback Functions
What Is the Purpose of a Plus Symbol Before a Variable
Using Jquery's Ajax Method to Retrieve Images as a Blob
JavaScript Regex Multiline Text Between Two Tags
How to Configure Cors in a Spring Boot + Spring Security Application
What's the Difference Between Window.Location and Document.Location in JavaScript
Using JavaScript's Atob to Decode Base64 Doesn't Properly Decode Utf-8 Strings
Find Mouse Position Relative to Element
Babel File Is Copied Without Being Transformed
Comparing Date Part Only Without Comparing Time in JavaScript
Getting All Variables in Scope
Jquery Click/Toggle Between Two Functions
How to Getelementbyclass Instead of Getelementbyid with JavaScript
Advantages of Createelement Over Innerhtml