Problems with changing constructor's prototype
I'm currently going through Stoyan Stefanov's book "Object-oriented JavaScript" and I've stumbled on an interesting problem. Here's the code:
var shape = {
type: 'shape',
getType: function() {
return this.type;
}
};
function Triangle(a, b, c) {
this.a = a;
this.b = b;
this.c = c;
this.type = 'triangle';
}
Triangle.prototype = shape; // changing the prototype object
Triangle.prototype.getPerimeter = function() {
return this.a + this.b + this.c;
}
var t = new Triangle(1, 2, 3);
t.constructor; // logs Object() instea开发者_JAVA技巧d of Triangle(a, b, c)
As you can see, here is a simple example of the constructor inhereting some properties from the prototype object. But the constructor property of object t points to the Object() object instead of Triangle(a, b, c), as it should have. If I comment the line with the prototype change, though, everything works fine. What's my problem? (Reread the whole prototype chapter in Object-oriented Javascript and JavaScript Patterns, couldn't find an answer). P.S. Sorry for my bad English, trying to practice it. :)
I will explain your code in two parts. Firstly, what is the constructor
property actually? Secondly, why doesn't it return Objects
in your code?
The constructor
property and Stoyan's mistake:
In Stoyan Stefanov's book, p150, he states:
prototype is a property that gets created as soon as you define the function. Its initial value is an empty object.
It is wrong. According to the JavaScript Definitive Guide Section 9.2
The initial value of the prototype property is an object with a single property. This property is named constructor and refers back to the constructor function with which the prototype is associated.
You can test it with Triangle.prototype.constructor
. It has been set when the function is defined.
Conclusion 1: the constructor
is in fact the property of Constructor.prototype
. In your case, it is Triangle.prototype.constructor
.
All the instances of Triangle
could access this property through the prototype chain. But these objects themselves don't have the constructor
property. The following code proves it:
function Triangle(a, b, c) {
this.a = a;
this.b = b;
this.c = c;
this.type = 'triangle';
}
var t = new Triangle(1, 2, 3);
t.hasOwnProperty('constructor');
>>false
t.__proto__.hasOwnProperty('constructor');
>>true
Conclusion 2: when you access the constructor
property of an instance, you get them from the prototype chain.
Why is it not working as you expected
You set Triangle.prototype
to shape
which doesn't contain the originalconstructor
property.
Thus, this time, when you query t.constructor
, it will resolve it in the following process:
- Look at it's own properties and find no
constructor
- Keep looking up to
t.__proto__
, which isTriangle.prototype
. You have set it toshape
which doesn't contain theconstructor
property. - Continue looking up along the prototype chain -
t.__proto__.__proto__
. It isTriangle.prototype.__proto__
, and it is resolved asObject.prototype
.Object.prototype
has theconstructor
property and it refers toObject
.
The "constructor" property, oddly enough, does not refer to the constructor of that object. Rather, it refers to the constructor of the object's prototype.
Here is the relevant doc page from Mozilla.
shape
is an object, so by doing this:
Triangle.prototype = shape;
You change Triangle
constructor to Object
You are basically using native/class based inheritance in your example here.
In Javascript, ( well from my understanding) when you use the new
keyword you create a function object with a constructor and an attached prototype object.
When you do this:
Triangle.prototype = shape;
You override the constructor method. You can use the console to observe the objects before and after your assignment of shape to Triangle.prototype
.
When you then do this:
t.constructor;
You are unable to view what you expect to see as the call to the constructor method finds the constructor method way down in the prototype chain.
shape is not a constructor function, but an object. It's constructor is the Object constructor.
If shape was a constructor you could set
Triangle.prototype=new shape;
and Triangle.prototype.constructor=Triangle, to override the shape.prototype.constructor you just set.
var shape = {
type: 'shape',
getType: function () {
return this.type
}
}
function Triangle(a, b, c) {
this.type = 'triangle'
this.a = a;
this.b = b;
this.c = c;
}
Triangle.prototype = shape;
Triangle.prototype.constructor = Triangle;
Triangle.prototype.getPerimeter = function () {
return this.a + this.b + this.c
}
var t = new Triangle(1, 2, 3)
console.log(t.constructor === Triangle) // true
console.log(shape.isPrototypeOf(t)) // true
console.log(t.getPerimeter()) // 6
console.log(t.getType()) // triangle
精彩评论