I build some base PopupBuilder class and want to implement it in some different usecases. For each case I create a class extends the base class.
So:
function PopupBuilder () {
var _this = this;
this.buildPopup = function () {
var popup = $('<div>', {
id: "popup",
class: "full-page",
click: clickHandler});
} //works fine
function acceptChanges () {}
function clickHandler (event) { // works fine
console.log(_this); // _this doesn't overrides by child class _this. Should it?
acceptChanges(event); // doesn't overrides by child class function as well
}
}
function ToServerSender (text) {
var _this = this;
function acceptChanges (event) {
// send data to server
}
}
ToServerSender.prototype = new PopupBuilder();
var updateFile = new ToServerSender();
updateFile.buildPopup();
I create an instance of child class, why I can't get to child class code and override/extend the base class?
How should I rewrite the code it will works?
You need to bear in mind that the way OOP works in JavaScript is vastly different from class-style OOP.
So let's break it down a bit:
var _this = this;
This assignment takes place when the new PopupBuilder()
constructor is called toward the end of the code you posted there. The this
here refers to an object of type PopupBuilder
, and through inheritance is essentially "shared" among all instances of ToServerSender
. Each ToServerSender
does not get its own copy of this _this
.
function acceptChanges () {}
This is just a function that exists inside your PopupBuilder
constructor. It's not a method. It can't be overridden by defining a new one in the ToServerSender
constructor:
console.log(_this); // _this doesn't overrides by child class _this. Should it?
As explained above, the _this
here is referring to the one instance of _this
that was created the one time you called the PopupBuilder
constructor and it is shared among all instances of ToServerSender
.
acceptChanges(event); // doesn't overrides by child class function as well
This is simply a closure to the acceptChanges
function inside the PopupBuilder
constructor. It can't be overridden.
So what can you do?
1. Use overridable methods.
One thing you need to do is assign methods to this
so that child objects can override them:
function PopupBuilder () {
var _this = this;
this.buildPopup = function () {} //works fine
this.acceptChanges = function () {}
this.clickHandler = function (event) {
console.log(this);
this.acceptChanges(event);
}
}
Here, we have the constructor assigning functions to properties of this
so that they can be inherited via the prototype chain and overridden as needed:
function ToServerSender (text) {
var _this = this;
// overrides prototype's .acceptchanges
this.acceptChanges = function (event) {
// send data to server
}
}
2. Create the child type's prototype using Object.create()
, not a constructor.
Using a constructor to create a prototype on a child type is a bit outdated. The new way to do this is to use Object.create
, to create a copy of the parent object's prototype:
ToServerSender.prototype = Object.create(PopupBuilder.prototype);
But this leaves the ToServerSender
prototype without all the stuff that was initialized in the PopupBuilder
constructor. This is actually a good thing, because the PopupBuilder
constructor was creating one copy of its private variables that were going to be shared between all of the ToServerSenders
in existence, potentially wreaking havok.
The solution to this is to...
3. Call the parent constructor from the child constructor:
function ToServerSender(text) {
PopupBuilder.call(this);
var _this = this;
this.a = "hello";
//this class overrides acceptChanges from PopupBuilder
this.acceptChanges = function(event) {
// send data to server
console.log(1);
console.log(_this);
}
}
What happens here is that ToServerSender
calls the PopupBuilder
constructor on itself, so that when _this
is created inside that constructor, it is referring to the ToServerSender
, and not some shared PopupBuilder
. This allows you to use it in closures and clear up some of the problems you were experiencing.
So those are the main things to keep in mind. I know this is a ton of stuff, but the more you use it, the easier it will become.
You need to attach the functions to the object.
function PopupBuilder () {
var _this = this;
this.buildPopup = function () {} //works fine
this.acceptChanges = function() { console.log(2);}
this.clickHandler = function(event) { // works fine
console.log(_this); // _this doesn't overrides by child class _this. Should it?
//Look a the difference here: when you use _this.acceptChanges it prints 2 when you use this.acceptChanges() it prints 1 to the console.
this.acceptChanges(event); // doesn't overrides by child class function as well
}
}
function ToServerSender (text) {
var _this = this;
this.a = "hello";
//this class overrides acceptChanges from PopupBuilder
this.acceptChanges = function(event) {
// send data to server
console.log(1);
console.log(_this);
}
}
ToServerSender.prototype = new PopupBuilder();
var updateFile = new ToServerSender();
updateFile.buildPopup();
Look at the above code to see how it works.
When in PopupBuilder
console.log prints _this
it uses a reference to this
in that scope (which contained the old acceptChanges
. However in ToServerSender
the scope has changed. This instance has other properties and is referred to by the this
object in that scope. This example show the difference between using a reference to this and the object this.
On a side note: when using more than 1 instance of an object use function references to avoid memory issues.
example
this.clickHandler = clickHandlerPopupBuilder
function clickHandlerPopupBuilder(event) {
console.log(_this);
this.acceptChanges(event);
}