JavaScript 'inheritance': constructor property is not correct - javascript

Please help explain the following result (tested on Firefox 3.6). How come this.constructor points to A inside prototype, if "this" is clearly of type B? I was under illusion that dictionary is traversed from topmost level down prototype chain, but it doesn't seem to be the case here:
A=function() {}
A.prototype.copy=function() {
return new this.constructor();
}
B=function() {}
B.prototype=new A();
var b=new B();
var bcopy=b.copy();
var cond1=bcopy.constructor==B // false
var cond2=bcopy.constructor==A // true

var b = new B;
b.constructor == A; // true
Thus, your copy() function is creating a new A. If, however, you add this line:
B.prototype.constructor = B;
...you will get the results you were hoping for.

Related

Iterate Constructor Chain Up

Assuming I have something like this:
function A() {}
function B() {}
B.prototype = Object.create(A.prototype);
function C() {}
C.prototype = Object.create(B.prototype);
var inst = new C();
I can now do inst instanceof C == true, inst instanceof B == true, instanceof C == true.
But how could I "iterate" the constructor functions up starting from the instance of C() so that it'd return function C(), function B(), function A() which I could then use to instantiate another instance.
You can iterate the prototypes by doing
for (var o=inst; o!=null; o=Object.getPrototypeOf(o))
console.log(o);
// {}
// C.prototype
// B.prototype
// A.prototype
// Object.prototype
However, that will only iterate the prototype chain. There is no such thing as a "constructor chain". If you want to access the constructors, you will need to set the .constructor property on the prototypes appropriately when inheriting:
function A() {}
function B() {}
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
function C() {}
C.prototype = Object.create(B.prototype);
C.prototype.constructor = C;
var inst = new C();
for (var c=inst.constructor; c!=null; (c=Object.getPrototypeOf(c.prototype)) && (c=c.constructor))
console.log(c);
// C
// B
// A
// Object
which I could then use to instantiate another instance
You would only need to know of C for that, not of the "chain". You may access it via inst.constructor if you have set C.prototype.constructor correctly.
However, it might be a bad idea to instantiate objects from arbitrary constructors; you don't know the required parameters. I don't know what you actually want to do, but your request might hint at a design flaw.
Go up the chain using the constructor property of the object's prototype.
For example, after your code:
C.prototype.constructor === A
is true, as is
inst.constructor.prototype.constructor === A
... and so forth.

Why my prototype chain become broken?

I'am learnig JavaScript and I need some help to understand what happens in my browser.
I have three JS classes:
function A(){}
function B(){}
function C(){}
B.prototype = new A();
C.prototype = new B();
a = new A(); // a instanceof A
b = new B(); // b instanceof A,B
c = new C(); // c instanceof A,B,C
But when I call:
A.prototype = new C();
// a is not instanceof A
// b is not instanceof A
// c is not instanceof A
// c is instanceof B
Could you please help me to understand what happens when I build such cycle prototype chain and why it breaks existing prototype chain?
Update:
I've found a special method to get prototype of the object, but it makes it not easier to understand.
Object.getPrototypeOf(a) // A{}
Object.getPrototypeOf(b) // A{}
Object.getPrototypeOf(c) // B{}
You have a circular reference, when you add A.prototype = new C();:
A references B
B references C
C references A
A references B
etc...
That's why your code breaks, since that can not work, there's no fix for it, that I know of.

Get the calling object of an instanced constructor?

I'm not sure if the title did this justice, but here is what I have...
function A() {};
A.prototype.B = function(){
this.backref = //?? -- should be set to "a"
};
var a = new A(); // specialized factory...
var b = new a.B(); // instantiate an "a" specialized version of B
var zztop = b.backref instanceof A; //would be true
I need to assign backref to the instance "a" that called the B constructor. How can I do this? I've looked through all the properties in Chrome debugger, and am not even sure it is possible. Anyone know how to do this? I need to do this because I need access to variables stored in "a". Or is there a better way to do something similar?
I'm not sure if there's a way to do this just using the properties given to you, but you can always make the backref one of the arguments to the B constructor:
A.prototype.B = function(backref) {
this.backref = backref;
}
var a = new A();
var b = new a.B(a);
Ugly, but it works.
What you are describing is called inheritance. You want object B to inherit the properties of object A.
function A() {
this.length = 3;
}
function B() {
this.height = 5;
}
B.prototype = new A();
B.prototype.constructor = B;
var obj = new B();
obj.height; // returns 5
obj.length; // returns 3
Here's a jsFiddle: http://jsfiddle.net/d6QeT/

JavaScript inheritance: when constructor has arguments

Using pure JavaScript to do inheritance, this is what I usually do:
function A() {}
A.prototype.run = function () {};
function B() {}
B.prototype = new A;
B.prototype.constructor = B;
Since there is no arguments to pass into the constructor, new A has nothing to complain about. Now, I haven't figured out a good way to do inheritance if the constructor has arguments to pass. For example,
function A(x, y) {}
A.prototype.run = function () {};
function B(x, y) {}
B.prototype = new A;
B.prototype.constructor = B;
I could pass some arbitrary values like:
B.prototype = new A(null, null);
In some cases, I may need to validate x and y in the constructor of A. In some extreme cases, I need throw errors when checking x or y. Then, there is no way for B to inherit from A using new A.
Any suggestions?
Thanks!
Well, if you want to make B.prototype an object that inherits from A.prototype, without executing the A constructor, to avoid all possible side-effects, you could use a dummy constructor to do it, for example:
function tmp() {}
tmp.prototype = A.prototype;
B.prototype = new tmp();
B.prototype.constructor = B;
You could create a function to encapsulate the logic of the creation of this new object, e.g.:
function inherit(o) {
function F() {}; // Dummy constructor
F.prototype = o;
return new F();
}
//...
B.prototype = inherit(A.prototype);
B.prototype.constructor = B;
If you target modern browsers, you could use the ECMAScript 5 Object.create method for the same purpose, e.g.:
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
//..
Although this is an old topic, I thought I'd respond anyway.
Two ways to do it:
Although the Pseudo Classical way is the most popular, it has its down sides since it needs to call the parent constructor once in the child constructor and once while inheriting the prototype. Besides, the child's prototype will contain all the properties of the parent constructor which will anyway get overwritten when the child constructor is called. My personal choice is Prototypal Inheritance.
1. Pseudo Classical Inheritance:
function A(x, y) {}
A.prototype.run = function () {};
function B(x, y) {
A.call(this,x,y);
}
B.prototype = new A();
B.prototype.constructor = B;
2. Prototypal Inheritance:
function A(x, y) {}
A.prototype.run = function () {};
function B(x, y) {
A.call(this,x,y);
}
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
The problem is that you can't easily create a prototype object for B since invoking the constructor of A can't be done. This is due to the parameters for the constructor being unknown before new B is executed. You need a dummy constructor function to construct a prototype for B that links to A's prototype.
B.prototype = (function(parent){
function protoCreator(){};
protoCreator.prototype = parent.prototype;
// Construct an object linking to A.prototype without calling constructor of A
return new protoCreator();
})(A);
Once you've got the prototype object for B set up, you need to ensure to call the constructor of A in the constructor of B.
function B(x, y) {
// Replace arguments by an array with A's arguments in case A and B differ in parameters
A.apply(this, arguments);
}
You should now be able to instantiate B by calling new B(x, y).
For a complete same including parameter validation in A see a jsFiddle.
In your original code you are setting B.prototype.constructor = B. I'm not getting why you are doing this. The constructor property does not influence the inheritance hierarchy for which the prototype property is responsible. If you want to have the named constructor contained in the constructor property you'd need to extend the code from above a little:
// Create child's prototype – Without calling A
B.prototype = (function(parent, child){
function protoCreator(){
this.constructor = child.prototype.constructor
};
protoCreator.prototype = parent.prototype;
return new protoCreator();
})(A, B);
Using the first definition of B.prototype you'd get the following results:
var b = new B(4, 6);
b.constructor // A
console.info(b instanceof A); // true
console.info(b instanceof B); // true
With the extended version, you'll get:
var b = new B(4, 6);
b.constructor // B
console.info(b instanceof A); // true
console.info(b instanceof B); // true
The cause for the different output is that instanceof follows up the whole prototype chain of b and tries to find a matching prototype object for A.prototype or B.prototype (in the other call). The b.constructor prototype does refers to the function that was used to define the instances prototype. In case you wonder why it does not point to protoCreator this is because its prototype was overwritten with A.prototype during the creation of B.prototype. The extended definition as show in the updated example fixes this constructor property to point to a more appropriate (because probably more expected) function.
For daily use, I'd recommend to discard the idea of using the constructor property of instances entirely. Instead do use instanceof since its results are more predictable/expected.
Consider this:
function B( x, y ) {
var b = Object.create( new A( x, y ) );
// augment b with properties or methods if you want to
return b;
}
And then
var b = new B( 12, 13 );
Now b inherits from an instance of A, which in turn inherits from A.prototype.
Live demo: http://jsfiddle.net/BfFkU/
Object.create isn't implemented in IE8, but one can easily manually implement it:
if ( !Object.create ) {
Object.create = function ( o ) {
function F() {}
F.prototype = o;
return new F();
};
}
This can be placed inside a ie8.js file which is loaded only for IE8 and below via conditional comments.

Constructor inheritance; what am I not getting here?

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?
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
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.
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.
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'.
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

Categories