Please consider such a code:
class a {
constructor() {
this.test = "test from a";
this.testA = "X";
}
mimi() {
console.log('aaaaaaaa', this.test, this.testA)
}
}
class b extends a {
constructor() {
super();
this.test = "test from b"
}
mimi() {
console.log('bbbbbbbb', this.test, this.testA)
}
}
class c extends b {
constructor() {
super();
this.test = "test from c"
}
mimi() {
console.log('cccccccc', this.test, this.testA)
}
meme() {
var test = kalreg.__proto__.__proto__;
test.mimi();
var test2 = kalreg.__proto__.__proto__.__proto__;
test2.mimi();
}
}
var kalreg = new c(); kalreg.mimi(); kalreg.meme();
The output I get is:
cccccccc test from c X
bbbbbbbb undefined undefined
aaaaaaaa undefined undefined
My object logic makes me use "a" as the most generic class, "b" that is a child to it, and "c" that is the child to "b". I want "c" to have all methods and properties of "b" and "a" BUT part of functionalities of "a" is overwritten by "b" so the only way "c" may have access to overwritten functionalities is to use prototype chain.
Unfortunately, my way doesn't work so the questions are:
In meme() function, how to avoid kalreg.proto - I've been told that such way of accessing prototype is bad and dangerous for the code.
In my opinion, there should be no "undefined" in output, however, there is.
The expected output is:
cccccccc test from c X
bbbbbbbb test from b X
aaaaaaaa test from a X
How to achieve it?
Thank you!
in meme() function, how to avoid kalreg.proto
Use super for the use cases where you really do need to access a property from the superclass (but note that that won't help with test and testA, because those aren't on the superclass or its prototype object, they're on the instance created with new c; more on that below).
Separately: Avoid __proto__. In those rare cases where you need to access an object's prototype, use Object.getPrototypeOf.
in my opinion there should be no "undefined" in output however there is. expected output is:
cccccccc test from c X
bbbbbbbb test from b X
aaaaaaaa test from a X
The output is correct, because the prototype objects on which you're calling mimi don't have a test or testA property. Only the object created with new c has those properties. And there is only one such object, so test will be "test from c" no matter which mimi you call, and testA will always be "X".
In a comment you've asked how there can only be one test when each constructor has this.test = ... in it. Let's look at what's in memory once you've done var kalreg = new c();:
+−−−−−−−−−−−−+
a−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+−>| (function) | +−−−>(Function.prototype)
| +−−−−−−−−−−−−+ |
| | __proto__ |−−+ +−−−−−−−−−−−−−+
| | prototype |−−−−−−−−−−+−>| (object) |
| +−−−−−−−−−−−−+ | +−−−−−−−−−−−−−+
| | | __proto__ |−−>...
+−−−−−−−−−−−−+ | | | constructor |−−>a
b−−−−−−−−−−−−−−−−−−−+−>| (function) | | | | mimi |−−>...
| +−−−−−−−−−−−−+ | | +−−−−−−−−−−−−−+
| | __proto__ |−+ +−−−−−−−−−−−−−+ |
| | prototype |−−−−−−−−+−>| (object) | |
| +−−−−−−−−−−−−+ | +−−−−−−−−−−−−−+ |
| | | __proto__ |−−+
+−−−−−−−−−−−−+ | | | constructor |−−>b
c−−−>| (function) | | | | mimi |−−>...
+−−−−−−−−−−−−+ | | +−−−−−−−−−−−−−+
| __proto__ |−+ +−−−−−−−−−−−−−+ |
| prototype |−−−−−−−+−>| (object) | |
+−−−−−−−−−−−−+ | +−−−−−−−−−−−−−+ |
| | __proto__ |−+
| | constructor |−−>c
+−−−−−−−−−−−−+ | | mimi |−−>...
kalreg−−−>| (object) | | | meme |−−>...
+−−−−−−−−−−−−+ | +−−−−−−−−−−−−−+
| __proto__ |−−+
| test |
| testA |
+−−−−−−−−−−−−+
As you can see, only the object kalreg points to has test or testA. Why? Because during the call to each constructor, this refers to that object; that's how new and super() work. So since this refers to that object, each constructor does its this.test = ... line, and since the one in c is the last one to run, it "wins."
You can access superclass versions of the mimi method, but since they all show the test property, they'll all show "test from c". To show different things, they'd have to have different properties to show. Additionally, with super you can only go one level up, so if you wanted to go two levels up, you'd either use a.prototype.mimi (or this.__proto__.__proto__.mimi explicitly, or put some facility in b to call a's mimi.
Example with different properties for each level, and with b providing superMimi so c can use a's mimi:
class a {
constructor() {
this.testA = "test from a (this.testA)";
}
mimi() {
console.log('aaaaaaaa', this.testA);
}
}
class b extends a {
constructor() {
super();
this.testB = "test from b (this.testB)";
}
mimi() {
console.log('bbbbbbbb', this.testB)
}
superMimi() {
return super.mimi();
}
}
class c extends b {
constructor() {
super();
this.testC = "test from c (this.testC)";
}
mimi() {
console.log('cccccccc', this.testC);
}
meme() {
super.mimi(); // Uses b's version
super.superMimi(); // Uses a's version
this.__proto__.__proto__.__proto__.mimi.call(this); // Also uses a's version
var p = Object.getPrototypeOf(this);
var p2 = Object.getPrototypeOf(p);
var p3 = Object.getPrototypeOf(p2);
p3.mimi.call(this); // Also uses a's version
}
}
var kalreg = new c();
kalreg.mimi();
kalreg.meme();
Related
var flagArray = [];
var flagTarget = document.getElementById("content");
var flag = {
init: function(country) {
if (country) {
this.country = country;
flagArray.push(Object.create(flag));
}
},
draw: function() {
var pos = flagArray.indexOf(this.country);
if (this.country === "norway") {
flagArray[pos] = '<div class="flag ' + this.country + '"><div class="part1"></div><div class="part2"></div><div class="part3"></div><div class="part4"></div></div>';
} else {
flagArray[pos] = '<div class="flag ' + this.country + '"><div class="part1"></div><div class="part2"></div></div>';
}
flagTarget.innerHTML += flagArray[pos];
},
};
flag.init("ivorycoast");
flag.init("sweden");
flag.init("denmark");
flag.init("finland");
flag.init("norway");
flagArray.forEach(function(flag) {
flag.draw();
});
I've been struggling with this school assignment for too many hours now. Why does the last flag.init value get written 5 times to the array?
It doesn't, but it seems like it does.
What's happening is that you're updating country on flag five times, and each time you're creating a new object that inherits from flag and pushing that on the array. So the end result is five different objects in the array, all of which inherit from flag, and none of which have their own country value. So the country you see on them is the one inherited from flag, which is the last value you assigned to this.country.
If you want them to inherit from flag but each have their own country value, you need to remember the object created with Object.create and set country on it:
init: function(country) {
if (country) {
var newFlag = Object.create(flag);
flagArray.push(newFlag);
newFlag.country = country;
}
},
(There's a more advanced way I show after the following diagrams.)
Side note: There's a separate problem in draw, this line:
var pos = flagArray.indexOf(this.country);
should just be
var pos = flagArray.indexOf(this);
Because the array contains objects, not country strings. That said, you don't need to use flagArray in draw at all.
Let's throw some ASCII-Art (actually, Unicode-art) at your current code to understand what you're seeing:
You start out with this in memory:
+−−−−−−−−−−−−−−−−−−−−−−−+
flag−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−>| (object) |
+−−−−−−−−−−−−−−−−−−−−−−−+
| init: (function) |
| draw: (function) |
+−−−−−−−−−−−−−−−−−−−−−−−+
+−−−−−−−−−−−+
flagArray−−−>| (array) |
+−−−−−−−−−−−+
| length: 0 |
+−−−−−−−−−−−+
Then after the first init call, you have:
+−−−−−−−−−−−−−−−−−−−−−−−+
flag−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+−−−−−−>| (object) |
| +−−−−−−−−−−−−−−−−−−−−−−−+
| | init: (function) |
| | draw: (function) |
| | country: "ivorycoast" |
| +−−−−−−−−−−−−−−−−−−−−−−−+
|
+−−−−−−−−−−−+ |
flagArray−−−>| (array) | |
+−−−−−−−−−−−+ |
| length: 1 | +−−−−−−−−−−−−−−−+ |
| 0 |−−−>| (object) | |
+−−−−−−−−−−−+ +−−−−−−−−−−−−−−−+ |
| [[Prototype]] |−−+
+−−−−−−−−−−−−−−−+
You've created a new object with flag as its prototype, and set flag's country to "ivorycoast".
After the second call to init, you have:
+−−−−−−−−−−−−−−−−−−−−−−−+
flag−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+−+−−−−>| (object) |
| | +−−−−−−−−−−−−−−−−−−−−−−−+
| | | init: (function) |
| | | draw: (function) |
| | | country: "sweden" |
| | +−−−−−−−−−−−−−−−−−−−−−−−+
| |
+−−−−−−−−−−−+ | |
flagArray−−−>| (array) | | |
+−−−−−−−−−−−+ | |
| length: 2 | +−−−−−−−−−−−−−−−+ | |
| 0 |−−−>| (object) | | |
| 1 |−+ +−−−−−−−−−−−−−−−+ | |
+−−−−−−−−−−−+ | | [[Prototype]] |−−+ |
| +−−−−−−−−−−−−−−−+ |
| |
| +−−−−−−−−−−−−−−−+ |
+−−>| (object) | |
+−−−−−−−−−−−−−−−+ |
| [[Prototype]] |−−−+
+−−−−−−−−−−−−−−−+
...and so on, until flagArray is filled with five separate objects, all using flag as their prototype, and flag has "norway" for its country property.
Here's that more advanced way to add country that I mentioned. If I were writing production code, I wouldn't use it for this (too verbose), but just for completeness: There's a second argument you can give Object.create that can have a list of properties to add to the new object. You specify the properties by giving their name and an object the specifies the various aspects of the property, like whether it's enumerable, whether it's read-only or writable, its initial value (or a getter and/or setter), etc. The writable, enumerable, and configurable features of a property are all defaulted false when you do this, but they default true when you create a property by just assigning to it. So to do the same thing we're doing with newFlag.country = country, we have to specify all those things and say true to make the property the same.
So, this does the same thing the version earlier using newFlag did, just without the temporary variable:
init: function(country) {
if (country) {
flagArray.push(Object.create(flag, {
country: {
enumerable: true,
configurable: true,
writable: true,
value: country
}
}));
}
},
Using Object.create(flag) you set the flag as prototype of each element in the array. The prototype is shared between all objects, so this.country gets updated on the prototype object in each init call and inherited by every element in the array.
To create a separate object you should try Object.assign, for example:
flagArray.push(Object.assign(Object.create(flag), { country : country }));
https://jsfiddle.net/p58189f1/
I am trying to get properties (excluding functions) those are only present in a subclass (child class) and not in the parent class in Javascript. I am using .hasOwnProperty() but it is also gives true for parent class's properties. I am running this in node.
Code:
class Model{
constructor(){
this.location = 'Gotham'
}
}
class Superhero extends Model{
}
const superhero = new Superhero()
superhero.alias = 'Batman'
superhero.realName = 'Bruce Wayne'
for (const property in superhero){
if (superhero.hasOwnProperty(property) && (typeof superhero[property] !== 'function')){
console.log(`${property} = ${superhero[property]}`)
}
}
Output:
location = Gotham
alias = Batman
realName = Bruce Wayne
Output I want to get:
alias = Batman
realName = Bruce Wayne
Please help!!
None of your properties is on the "parent" or "child" class, they're all on the instance (object) created via new Superhero. There is no way to tell, looking at the instance, that location was added to the instance by the Model constructor but the other two were added to it by the Superhero constructor. That information is just not part of the object model in JavaScript.
As of where you start your loop, here's what you have in memory (with some details omitted):
+−−−−−−−−−−−−+
Model−−−−−−>| (function) |
+−−−−−−−−−−−−+ +−−−−−−−−−−−−−−−+
| prototype |−−−−−−−−−−−−−−−−−−−−−−−−−−−+−−>| (object) |
+−−−−−−−−−−−−+ / +−−−−−−−−−−−−−−−+
| | [[Prototype]] |−−−−>Object.prototype
| +−−−−−−−−−−−−−−−+
|
|
+−−−−−−−−−−−−+ |
Superhero−−>| (function) | |
+−−−−−−−−−−−−+ +−−−−−−−−−−−−−−−+ |
| prototype |−−−−+−>| (object) | |
+−−−−−−−−−−−−+ / +−−−−−−−−−−−−−−−+ |
| | [[Prototype]] |−−+
| +−−−−−−−−−−−−−−−+
|
+−−−−−−−−−−−+
\
+−−−−−−−−−−−−−−−−−−−−−−−−−+ |
superhero−−>| (object) | |
+−−−−−−−−−−−−−−−−−−−−−−−−−+ |
| [[Prototype]] |−−−+
| location: "Gotham" |
| alias: "Batman" |
| realName: "Bruce Wayne" |
+−−−−−−−−−−−−−−−−−−−−−−−−−+
As you can see, all the properties are on the instance itself.
If instance were on a prototype of the instance, you'd be able to differentiate them, but not with what you have where they're all properties on the instance itself.
You can move location to a prototype (specifically, the prototype of the prototype of the instance) by making it an accessor property:
class Model {
constructor() {
// This makes _location non-enumerable and non-configurable
Object.defineProperty(this, "_location", {
value: "Gotham",
writable: true
});
}
get location() {
return this._location;
}
set location(value) {
this._location = value;
}
}
// Make our "location" accessor property enumerable (if you want it to be).
// By default, accessors defined in class notation are non-enumerable.
{
const desc = Object.getOwnPropertyDescriptor(Model.prototype, "location");
desc.enumerable = true;
Object.defineProperty(Model.prototype, "location", desc);
}
class Superhero extends Model {
}
const superhero = new Superhero();
superhero.alias = 'Batman';
superhero.realName = 'Bruce Wayne';
for (const property in superhero){
if (superhero.hasOwnProperty(property) && typeof superhero[property] !== 'function') {
console.log(`${property} = ${superhero[property]}`)
}
}
In that example, since we have to store the value of location somewhere, I created a _location property to store it in. That's still on the instance, though, and could be set without going through the accessors. (It doesn't show up in the for-in loop because I made it non-enumerable.) If we wanted to really protect it, we'd have to store it separately from the instance, probably by using a WeakMap keyed by the instance itself. But going that far isn't usually necessary.
As stated in T.J. Crowder's great explanation, location belongs to child subclass, because when child class is instantiated, location is set on child class instance (this).
A simple way to make a property belong to parent class is to set it on class prototype:
class Model{}
Model.prototype.location = 'Gotham';
class Superhero extends Model{}
// 'location' in new Superhero() === true
// new Superhero().hasOwnProperty('location') === false
I try to achieve an object inheritance, where each object shares the same functionality but contains different values/containers. Doing so I discovered a strange behavior; each individual object works like a charm but they all share the same options. Do clarify what I mean, I added a sample code.
What I want to do is read the data-value from each element and bind it to the options. Those are required inside some functions to work correctly!
<div class="first" data-value="first div"></div>
<div class="second" data-value="second div"></div>
<script>
var original = {
options: {
value: null,
factor: 13
},
init: function (container) {
this.options.value = container.getAttribute('data-value');
}
};
var variation = Object.create(original);
variation.options.factor = 37;
var firstDiv = Object.create(variation);
var secondDiv = Object.create(variation);
firstDiv.init(document.getElementsByTagName('div')[0]);
secondDiv.init(document.getElementsByTagName('div')[1]);
alert('firstDiv.options === secondDiv.options: ' + (firstDiv.options === secondDiv.options)); // but should be false!
</script>
Please note, that this just shows a little part of the actual objects. All the other parts are, in my opinion, irrelevant.
I hope it's clear what the problem is. Also that I use Object.create() on purpose.
objects contain same values but shouldn't
They should, because you haven't changed the options property on variation, so it still points at the same object that the original.options points to.
When you do this:
var original = {
options: {
value: null,
factor: 13
},
init: function (container) {
this.options.value = container.getAttribute('data-value');
}
};
Here's what you get in memory (some details omitted):
+−−−−−−−−−−−−−−−−−−−−+
+−−−−−−−−−−−−+ +−−−>| (Object.prototype) |<−+
original−−−>| (object) | | +−−−−−−−−−−−−−−−−−−−−+ |
+−−−−−−−−−−−−+ | |
| __proto__ |−−−+ +−−−−−−−−−−−−−+ |
| options |−−−−−−−>| (object) | |
| init |−−−+ +−−−−−−−−−−−−−+ |
+−−−−−−−−−−−−+ | | __proto__ |−−−−−−−−−+
| | value: null |
| | factor: 13 |
| +−−−−−−−−−−−−−+
|
| +−−−−−−−−−−−−+
+−−−−| (function) |
+−−−−−−−−−−−−+
...where __proto__ is a pseudo-property showing what the object's prototype is (specifically, the value of what the spec calls the object's internal [[Proto]] property). (On browsers, in ES2015, there is actually a __proto__ accessor, but it shouldn't be used.)
Then when you do this:
var variation = Object.create(original);
You have:
+−−−−−−−−−−−−+
variation−−>| (object) |
+−−−−−−−−−−−−+
| __proto__ |−−+
+−−−−−−−−−−−−+ |
|
+−−−−−−−−−−−−−−−−−−+ +−−−−−−−−−−−−−−−−−−−−+
| +−>| (Object.prototype) |<−+
\ +−−−−−−−−−−−−+ | +−−−−−−−−−−−−−−−−−−−−+ |
original−−−>| (object) | | |
+−−−−−−−−−−−−+ | |
| __proto__ |−−−−−+ +−−−−−−−−−−−−−+ |
| options |−−−−−−−>| (object) | |
| init |−−−+ +−−−−−−−−−−−−−+ |
+−−−−−−−−−−−−+ | | __proto__ |−−−−−−−−−+
| | value: null |
| | factor: 13 |
| +−−−−−−−−−−−−−+
|
| +−−−−−−−−−−−−+
+−−−−| (function) |
+−−−−−−−−−−−−+
You can see how both original and variation are still pointing at the same options object.
If you want separate options objects, you have to create a new object. You could make variation.options use original.options as its prototype:
var variation = Object.create(original);
variation.options = Object.create(original.options);
...but then you'd have to do it again for firstDiv and secondDiv:
var firstDiv = Object.create(variation);
firstDiv.options = Object.create(variation.options);
var secondDiv = Object.create(variation);
secondDiv.options = Object.create(variation.options);
...which suggests we need to do something different.
You could have a function you use to do it:
function createVariation(source) {
var v = Object.create(source);
v.options = Object.create(source.options);
return v;
}
var variation = createVariation(original);
var firstDiv = createVariation(variation);
var secondDiv = createVariation(variation);
...but you might look at constructor functions or maker functions, and a fairly typical pattern called the "extend" function (such as jQuery's or Underscore's).
Neither firstDiv nor secondDiv has its own options property. Both inherit options from original. You can test this by observing that all of the expressions below are false:
variation.hasOwnProperty("options") // false
firstDiv.hasOwnProperty("options") // false
secondDiv.hasOwnProperty("options") // false
Your init function does this.options.value = ..., but consider for a moment only the expression this.options. You never create another options object, so when you ask for firstDiv or secondDiv for its options object, that escalates the this.options lookup to the original prototype object.
Thus, when you access the options property of either firstDiv or secondDiv, you are causing the property lookup to escalate to original (the first object in the prototype chain that really has an options property). This means that firstDiv.options and secondDiv.options are the exact same object (namely, original.options). Therefore, it follows that when you set secondDiv.options.value, you are also setting firstDiv.options.value.
The solution here is to ensure that firstDiv and secondDiv explicitly have their own options property. However, you also want that options value to itself inherit from variation.options, and you want variation to have its own options value as well, which inherits from original.options. T.J. Crowder's answer addresses this in detail with his createVariation method:
function createVariation(source) {
var v = Object.create(source);
v.options = Object.create(source.options);
return v;
}
var variation = createVariation(original);
var firstDiv = createVariation(variation);
var secondDiv = createVariation(variation);
Tell the difference between the two types of inheritance?
function inherit(c,p){
var F =function(){}
F.prototype=p.prototype;
c.prototype=new F();
}
AND
function inherit(c,p){
c.prototype=p.prototype;
}
What are the pros and cons of each if there are such?
In the second one, both c and p end up using the same object as their prototype property. So adding something to that object affects instances created with both new c and new p, because those instances get their underlying prototype from that prototype property on the function. That's a problem, the child shouldn't affect instances of the parent like that.
So for example, using the second one, we have this problem:
function Parent() {
}
function Child() {
}
inherit(Child, Parent);
Parent.prototype.question = "Life, the Universe, and Everything";
Child.prototype.answer = 42;
var par = new Parent();
var ch = new Child();
console.log(par.question); // "Life, the Universe, and Everything"
console.log(ch.question); // "Life, the Universe, and Everything"
console.log(par.answer); // 42 <=== WRONG, Parent instances don't have the property
console.log(ch.answer); // 42
The code above creates this situation in memory (let's use a property called __proto__ to represent an object's underlying prototype; that's what the upcoming ES6 spec does):
The second one: Wrong
+-----------------+
| Function Parent |
+-----------------+ +---------------+
| prototype |----+++-->| Object |
+-----------------+ ||| +---------------+
||| | question: ... |
+------------+ ||| | answer: 42 |
| Parent par | ||| +---------------+
+------------+ |||
| __proto__ |---------+||
+------------+ ||
||
+-----------------+ ||
| Function Child | ||
+-----------------+ ||
| prototype |-----+|
+-----------------+ |
|
+-----------+ |
| Child ch | |
+-----------+ |
| __proto__ |------------+
+-----------+
With the first one, the c.prototype is a new object that has p.prototype as its underlying prototype. Adding things to c.prototype won't affect instances created with new p, just ones created with new c. But adding things to p.prototype affects both, which makes sense if c is meant to inherit from p.
function Parent() {
}
function Child() {
}
inherit(Child, Parent);
Parent.prototype.question = "Life, the Universe, and Everything";
Child.prototype.answer = 42;
var par = new Parent();
var ch = new Child();
console.log(par.question); // "Life, the Universe, and Everything"
console.log(ch.question); // "Life, the Universe, and Everything"
console.log(par.answer); // undefined, Parent instances don't have the property
console.log(ch.answer); // 42
That sets this up in memory:
The first one
+-----------------+
| Function Parent |
+-----------------+ +---------------+
| prototype |----+---->| Object |<-+
+-----------------+ | +---------------+ |
| | question: ... | |
+------------+ | +---------------+ |
| Parent par | | |
+------------+ | |
| __proto__ |---------+ |
+------------+ |
|
+-----------------+ |
| Function Child | |
+-----------------+ +---------------+ |
| prototype |------+-->| Object | |
+-----------------+ | +---------------+ |
| | __proto__ |--+
+-----------+ | | answer: 42 |
| Child ch | | +---------------+
+-----------+ |
| __proto__ |------------+
+-----------+
What are the pros and cons of each if there are such?
The second one is effectively wrong. :-)
Side note: In an ES5-enabled environment, the first one can be simpler:
function inherit(c,p){
c.prototype = Object.create(p.prototype);
}
Side note 2: To be more thorough, the first one should look like this:
function inherit(c,p){
var F =function(){}
F.prototype=p.prototype;
c.prototype=new F();
c.prototype.constructor = c;
}
or this:
function inherit(c,p){
c.prototype = Object.create(p.prototype);
c.prototype.constructor = c;
}
The constructor property of the object referenced from a Function object's prototype property should point to the function. This is how functions are set up in the specification, although it doesn't actually then use the constructor property for anything. Some people may have code that uses it, though, and expects the spec's setup to be there.
Consider the following snippet:
function Dog(){this.tail = true;}
var benji = new Dog();
var rusty = new Dog();
Dog.prototype.say = function(){return 'Woof!';}
alert(benji.say()); // this alerts woof!
alert(rusty.say()); // this alerts woof!
alert(benji.constructor.prototype.constructor); // THIS ALERTS THE DOG FUNCTION
alert(typeof benji.constructor.prototype.tail) // THIS ALERTS UNDEFINED
Why does the benji.constructor.prototype.constructor alert the dog function?
Shouldn't it be an Object() constructor?
It's simple:
+-----------------+ tail
| |-----------------> [true]
| benji |
| |
+-----------------+
|
| __proto__
|
v
+-----------------+ constructor +-----------------+
| |------------------>| |
| Dog.prototype | | Dog |
| |<------------------| |
+-----------------+ prototype +-----------------+
|
| __proto__
|
v
+-----------------+ constructor +-----------------+
| |------------------>| |
| Object.prototype| | Object |
| |<------------------| |
+-----------------+ prototype +-----------------+
benji.constructor is Dog.
benji.constructor.prototype is Dog.prototype.
benji.constructor.prototype.constructor is Dog.
benji.constructor.prototype.constructor.prototype is Dog.prototype.
benji.constructor.prototype.constructor.prototype.constructor is Dog.
This goes on forever. Instead what you want is Object.getPrototypeOf:
Object.getPrototypeOf(benji).constructor is Dog.
Object.getPrototypeOf(Object.getPrototypeOf(benji)).constructor is Object.
Since Object.getPrototypeOf is iterated you could create a special function for it:
function getPrototypeOf(n, object) {
if (n === 0) return object;
else return getPrototypeOf(n - 1, Object.getPrototypeOf(object));
}
Now you can use it as follows:
getPrototypeOf(1, benji).constructor; // Dog
getPrototypeOf(2, benji).constructor; // Object
That's all folks.
benji.constructor resolves to Dog. Note that benji doesn't actually have it's own constructor property: it inherits the constructor property from its prototype. benji.constructor really is Dog.prototype.constructor, just like benji.say is really Dog.prototype.say.
Thus, benji.constructor.prototype is actually Dog.prototype, which is the prototype where benji inherits prototypal properties from.
Finally, therefore, benji.constructor.prototype.constructor is really Dog.prototype.constructor, which is the same as benji.constructor.
By contrast, benji does have his own tail property. That is set directly on the instance when it is constructed, and it is not inherited from the prototype.
This is because the prototype of Dog doesn't contain the property. Instead, you are setting it for every new instance, inside the constructor function. To have tail in the prototype, you'd have to define it there:
Dog.prototype.tail = true;