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.
Related
in this case
var A = function(){
this.d = 123;
}
A.prototype.c = 999;
A.prototype.d = 333;
var B = (new A()).constructor;
console.log(""+B);
console.log(new A().d); // 123
console.log(new B().d); // 123
console.log(new A().c); // 999
console.log(Object.getPrototypeOf(new B()).c); // 999 why?
A and B share same constructor
but B is not A, why has same prototype of A?
in this case
var A = function(){
this.d = 123;
}
A.prototype.c = 999;
A.prototype.d = 333;
var B = A.constructor;
console.log(""+B);
console.log(new A().d); // 123
console.log(new B().d); // undefined
console.log(B.d); // still undefined
console.log(new A().c); // 999
console.log(Object.getPrototypeOf(new B()).c); // undefined
B is constructor of A and not of his instance
what is B? how to access constructor of A with no instance of A?
When you call new A(), you create a new A object whose prototype is A.prototype. When you ask for (new A()).constructor, you're accessing the constructor property from the prototype chain of that A instance; this would be A.prototype.constructor.
A itself is a Function object. That is to say: A is an instance of Function. When you ask for A.constructor, you're accessing the constructor property from the prototype chain of that Function instance; this would be Function.prototype.constructor.
In your first case, B and A are references to the exact same function. It's totally expected that the results of new A() and new B() would have the same properties and the same prototype chain.
In your second example, B is the Function constructor -- i.e., a function that constructs functions. Calling new B() creates a new Function object. Thus, the result of new B() has none of the same properties as an A instance.
To tell the difference you might want to look at what is A and what is new A():
c = new A(); // This is an instance of A. The constructor property is A.
console.log(c.constructor) // function() { this.d = 123; }
console.log(new c.constructor()) // Creates another instance of A.
console.log(Object.getPrototypeOf(new c.constructor())) // A {c: 999, d: 333}
var c = A; // This is a function. The constructor property is a base Function.
console.log(c.constructor) // function Function() { [native code] }
console.log(new c.constructor()) // Creates instance of base Function.
console.log(Object.getPrototypeOf(new c.constructor())) // function Empty() {}
Without the new operator on your custom constructor (A) you are not creating an instance of A.
More information on new operator: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new
Even though it's already answered, I did not see it made clear that B is A (oops, its late missed apsillers mentioned it):
var A = function(){};
var B = (new A()).constructor;
console.log(B===A,B===A.prototype.constructor
,A.prototype.constructor ===A);//true true true
//Something.prototype.constructor is a circular reference as constructor
//references Something
console.log(A.prototype.constructor.prototype.constructor.prototype
.constructor.prototype.constructor === A);//true
Constructor comes with prototype and is set to the constructor function but you can overwrite it (usually done when inheriting so you usually see it repaired after inheriting)
Child.prototype=Parent.prototype;
//Child.prototype.constructor now incorrectly refers to Parent
console.log(Child.prototype.constructor===Parent);//true
//repair constructor
Child.prototype.constructor=Child;
More on inheritance, constructor functions and prototype here.
Since all objects have a prototype (unless created with Object.create(null)) all objects have a constructor property pointing to the function that created them:
console.log([].constructor===Array);
var arr = new [].constructor(1,2,3);//same as new Array(1,2,3)
console.log(arr);//[1,2,3]
//the following temporarily casts the string to String
console.log("hello".constructor===String);//true
//same with numbers
console.log(1..constructor===Number);//true
console.log(function(){}.constructor === Function);//true
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.
When making the prototypal inheritance, it's asked to refer the child's constructor back to itself below,
A = function() {}
B = function() {}
B.prototype = new A;
B.prototype.constructor = B;
What would take adverse effect if not?
EDIT
As #GGG & #CMS have explained, the constructor alignment takes no
effect on creating the child object by new Child(...), but is
necessary to correctly reflect the child object's constructor later.
#GGG has also suggested a defensive alternative to extend
the prototype chain. While Child.prototype = new Parent(...)
includes parent's properties to child, Child.prototype =
Object.create(Parent.prototype) doesn't.
First, please don't do this:
B.prototype = new A;
Do this instead (shim Object.create for old browsers):
B.prototype = Object.create(A.prototype);
As for constructor, nothing will break if you don't do this, but if you don't:
A = function() {};
var a = new A();
console.log(a.constructor); // A
B = function() {};
var b = new B();
console.log(b.constructor); // A (!)
...setting the constructor property of the prototype back to the actual constructor function allows you to do this:
B.prototype.constructor = B;
var b = new B();
console.log(b.constructor); // B
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.
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.