Why Wouldn't I Use Child.Prototype = Parent.Prototype Rather Than Child.Prototype = New Parent(); for JavaScript Inheritance

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 both child.prototype and parent.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



Leave a reply



Submit