There must be something I don't understand about the JS object model.
From these resources:
I have gathered what I think, or thought, was an accurate mental representation of the object model. Here it is:
All objects have a property, which the docs refer to as [[Prototype]]
. [[Prototype]]
can be thought of as a reference to the object's parent. More accurately:
The reference to the [parent's] prototype object is copied to the internal
[[Prototype]]
property of the new instance. (source 1)
You can get access to the [[Prototype]]
property of the child with Object.getPrototypeOf(child)
The value returned here will be a reference to the parent's prototype (not its internal [[Prototype]]
property, but its actual prototype)
obj.prototype
is different from the object's internal [[Prototype]]
property. It acts like the blueprints used to make instances of this exact object, while its [[Prototype]]
property points to the blueprints used to make instances of its parent.
Parent.prototype === Object.getPrototypeOf(child); //true
To elaborate:
If you add a function to child.prototype
the function will be available to child
and any of it's children.
If you add a function to parent.prototype
, which is equivalent to adding a function to Object.getPrototypeOf(child)
, then the function will be available to parent
and all of it's children, which includes child
and all of its siblings
.
You can use Object.create()
to create a new object with whatever [[Protoype]]
property you want. So you can use it as a way to implement inheritance. See source 2 for an example.
With this in mind, I wanted to get a working example of my own going. My plan was to create a parent 'class' and then make a child 'class' that inherited from it.
I wanted the child class to implement a method, which overloaded a method from the parent. The caveat is that I want the child's version of the method to call the parent's version of the method and then do some extra stuff.
Here is what I came up with, see below for the issues associated with it:
var Parent = function() {};
Parent.prototype.myF = function() {
console.log('derp');
};
function Child() {
Parent.call(this);
};
//make [[prototype]] of Child be a ref to Parent.prototype
Child.prototype = Object.create(Parent.prototype);
//need to explicitly set the constructor
Child.prototype.constructor = Child;
Child.prototype.myF = function() {
Object.getPrototypeOf(this).myF();
console.log("did I derp??");
};
childInstance = new Child();
childInstance.myF();
It appears to be the case that when I attempt to overload Parent.myF()
, while I am overloading it, I am actually modifying the original function at the same time. This appears to be the case because the logged results are:
'derp'
'did I derp??'
'did I derp??'
presumably the first occurance of 'did I derp??'
is coming from a modified version of the parent's function, which I don't mean to do, then the second version is coming from the child's function.
Can anyone elaborate on why this is happening?
Great question, it took a bit of testing and researching to find it out.
I changed your code a little bit to find out which function is called when:
var Parent = function() {};
Parent.prototype.myF = function() {
console.log('derp');
};
function Child() {
Parent.call(this);
this.name = 'Test'; // this is a new test property
};
//make [[prototype]] of Child be a ref to Parent.prototype
Child.prototype = Object.create(Parent.prototype);
//need to explicitly set the constructor
Child.prototype.constructor = Child;
Child.prototype.myF = function() {
console.log(this); // here I want to find out the context, because you use it in the next line
Object.getPrototypeOf(this).myF();
console.log("did I derp??");
};
childInstance = new Child();
childInstance.myF();
You can check out the JSFiddle and try it for yourself: http://jsfiddle.net/Lpxq78bs/
The crucial line in your code is this:
Child.prototype.myF = function() {
Object.getPrototypeOf(this).myF(); // this one
console.log("did I derp??");
};
After doing a console.log(this);
to find out what this
refers to, I saw that it changes between the first and the second output of did I derp??
.
I got the following output:
Object { name: "Test" }
Object { constructor: Child(), myF: window.onload/Child.prototype.myF() }
"derp"
"did I derp??"
"did I derp??"
Since I added a 'name' property to the Child
constructor, it would only be around if I am looking at an instance of Child
, not at its .prototype
.
So the first line of the Output means that the current this
context is indeed the childInstance
. But the second one is neither the childInstance
, nor the Parent.prototype
:
Call (myF
of childInstance
): this
refers to the childInstance
. Object.getPrototypeOf(this).myF();
then looks for the [[Prototype]]
of the childInstance
, which is the Child.prototype
, not the Parent.prototype
.
Output: 'did I derp??'
Call (myF
of Child.prototype
): this
refers to the Child.prototype
, which is the childInstances
[[Prototype]] Property. So the second call of Object.getPrototypeOf(this).myF();
finally returns the Parent.prototype
(sort of). Output: 'did I derp??'
Call (myF
of Parent.prototype
instance created by Object.create
): Finally, the myF
on the parent is called. Output: 'derp'
Since your console.log("did I derp??")
comes after the myF
function call, the output is in reverse order. The following graphic illustrates how the code is traversed:
So your assumption about what Object.getPrototypeOf(this).myF();
refers to, was wrong.
By @LukeP: https://jsfiddle.net/Lpxq78bs/28/
To avoid this confusion, and since you are working with a classical inheritance pattern, you could have a look at ES6 Classes. The following would be a rough example of what you are trying to do:
class Parent {
constructor() {
}
myF() {
console.log('derp');
}
}
class Child extends Parent {
constructor() {
super();
}
myF() {
super.myF();
console.log("did I derp??");
}
}
var childInstance = new Child();
childInstance.myF();
I hope this helps in understanding what happens.