views:

319

answers:

5

I'm trying to understand inheritance in Javascript, but this code (tested in Firebug) doesn't work the way I'm expecting. What am I not understanding about it?

var A = function(v){
  this.v = v || 'foo';
}

A.prototype.shout = function(){ alert(this.v); }

var B = function(){};

B.prototype = new A;

var test = new B('bar')
test.shout() // returns 'foo'

What I'm expecting is that when I assign test the property this.v is set to "bar". As far as I understand JS prototypical inheritance setting B's prototype to A's means everything in B is overwritten. I would expect then that it's constructor is overwritten, and that calling new B('bar') would execute A's constructor with the parameter "bar".

If my understanding is incorrect, could someone please correct me. It would also be good to find the solution to what I'm looking to do:

// constructors from above
B.prototype.yell = function(){ alert('hello world'); }
var test1 = new B('bar'), test2 = new B('spam'), test3 = new B('eggs');

...so that the JS objects I'm creating follow a similar inheritance pattern to standard OOP and therefore B would inherit A's constructor and methods.

Edit:

After reading the comments, I believe a better question would be to ask how would one overwrite B's constructor with A's?

+2  A: 

Following is a constructor. It runs when you call new A.

var A = function(v){
  this.v = v || 'foo';
}

In B constructor is function(){}; So you are not setting this.v

To achieve result you are trying you should follow this pattern:

var A = function(v){
  this.v = v || 'foo';
}

A.prototype.shout = function(){ alert(this.v); }

var B = function(v){
    B.prototype.constructor.call(this,v); // here you call A constructor 
};

B.prototype = new A;


var test = new B('bar')
test.shout() // returns 'bar
Eldar Djafarov
As far as I understood it, when setting `B.prototype` it copied *everything* from `A`? Is that incorrect?
digitala
yes. But when you call new Class() you are just calling a constructor. And in B case constructor is function(){} do nothing.
Eldar Djafarov
+1  A: 

When you set

var B = function(){};

you create a parameterless constructor B. Then when you do

B.prototype = new A;

you are calling the A constructor with no parameters (causing this.v = 'foo'), and then causing B's prototype to point to the v and shout from A. However, B as a constructor hasn't changed. This can be seen by adding some parameters to the definition of 'B', for example:

var B = function(x){this.v += x};

should produce 'foobar'.

Addendum: The closest way I can think of to "copy" the constructor is actually to create both constructors with some third-party function and assign to both A & B. Example:

var objmaker = function(defaultval) {
    return function(v) { this.v = v || defaultval; };
}

var A = objmaker('foo');
A.prototype.shout = function(){ alert(this.v); }

var B = objmaker('bar');
B.prototype = A.prototype; // B can now shout()

var testA1 = new A('A');
testA1.shout(); // returns 'A'
var testA2 = new A();
testA2.shout(); // returns default 'foo'

var testB1 = new B('B');
testB1.shout(); // returns 'B'
var testB2 = new B();
testB2.shout(); // returns default 'bar'

It's not really copying, as you can see by the different default values, but it is one way to ensure the two definitions stay in sync.

James M.
Is there, then, a way to get `B`'s constructor to be replaced with `A`'s?
digitala
B IS the constructor. B is not a class, it is a constructor function. Javascript does not have classes.
Breton
Who said anything about classes?
James M.
Djko did up there, but good point. The clause "B's constructor" implies that phillip is thinking of B as a seperate entity from the constructor. Well if B is not a constructor, what is it? Most likely a class! Except that B *is* the contructor, and it's nonsensical to ask about modifying B's constructor as if B and the constructor are not one and the same.
Breton
A: 

Try

var test2 = new A('baz');
test2.shout(); //returns baz

I think when you make 'var B' a new function, rather than making it a' new A', you wipe out the 'constructor' you made 'A' as. I'm not sure how the original is still getting invoked to set test's instance of v to 'foo'.

NSherwin
I don't think that's the solution, for example: `A` contains common methods which are inherited by `B` and `C`. `B` defines additional methods which `A` doesn't have, as does `C`. So I need to create an instance of `B` but pass use `A`'s constructor.
digitala
A: 

How about instead of trying to make javascript work like some other language, you could maybe learn something new from it, and use so it works like javascript?

have a look at this: http://javascript.crockford.com/prototypal.html

Breton
I *am* trying to use it like javascript. I understand an idiom in one language, and I'm asking for help in replicating that idiom in another.
digitala
But that isn't like javascript. That's like that other language. The idiom doesn't make sense in javascript. Read the link, and you'll understand. It has a link inside it to another page with instructions for what you want to do exactly.
Breton
I mean, you could be asking how do you do "Goto" in javascript, would I be unreasonable in pointing out that javascript doesn't have a GOTO command?
Breton
Not at all. However, wouldn't it be better to say "Sorry, its not really possible to use GOTO in Javascript. You can get a similar effect by doing *this*..." (although I'd honestly expect someone to slap me if I asked about GOTO in JS ;).
digitala
I did something better. I gave you a link to instructions for both replicating your idiom, and the more javascript like idiom. Do you not appreciate my ability to reuse the work of others?
Breton
A: 

Instead of setting the prototype to a new instance of A, make B's prototype delegate up to A's prototype. Crockford calls this beget, it's implemented as dojo.delegate in Dojo. It's a simple function that looks like this:

function delegate(o){
  F = function(){}
  F.prototype = o;
  return new F;
}

And you can use it to create this link between prototypes:

var B = function(){}
B.prototype = delegate(A.prototype);

By doing this, the A constructor never gets called, and the v value is never set. This limits inheritance to only the prototype, and not the prototype plus constructor.

pottedmeat