Reputation: 1012
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:
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!
Upvotes: 3
Views: 78
Reputation: 1074178
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();
Upvotes: 2