Javascript inheritance - call to child method always invokes method of parent - javascript

This is my first time working with inheritance in Javascript. I have a classical parent-child relation between an object "LatchOn" that does one thing, and an object "LatchOnSlider" that has all the functionality of the base class and then some more. stripped down code:
/*creating the instances*/
var latchon = NewLatchOn(); //arguments not important for this
var latchonslider = NewLatchOnSlider();
//factory function
function NewLatchOn(element_id, container_id, screenpos, release)
{
var newLatchOn = new LatchOn(element_id, container_id, screenpos, release);
newLatchOn.startTimer();
}
function LatchOn(element_id, container_id, screenpos, release)
{
//initialise stuff
}
LatchOn.prototype.startTimer = function()
{
var that = this;
setInterval(function(){that.checkPos()}, 10);
}
LatchOn.prototype.checkPos = function()
{
//do stuff
}
LatchOnSlider.prototype.constructor = LatchOnSlider;
//factory function
function NewLatchOnSlider(element_id, container_id, image_class, screenpos)
{
LatchOnSlider.prototype = Object.create(LatchOn.prototype);
var newSlider = new LatchOnSlider(element_id, container_id, image_class, screenpos);
newSlider.startTimer();
return newSlider;
}
function LatchOnSlider(element_id, container_id, image_class, screenpos)
{
LatchOn.call(this, element_id, container_id, screenpos, "CONTAINER");
//initialise own stuff
}
LatchOnSlider.prototype.startTimer = function()
{
var that = this;
setInterval(function(){that.checkPos()}, 10);
}
/*keeps track of when to switch images*/
LatchOnSlider.prototype.checkPos = function()
{
alert("child process called");
}
The objects have to listen to the scrollposition in the browser and act according to that information. So they have a timer running. But as it wouldn't be optimal for two timers running for the inherited object, I figured I could just run one timer that calls the function on the child object, which will invoke the base function and then do its own stuff (invocation of base function not yet in the code. I haven't gotten that far yet...).
In order to not have to start the timer separately for every instance, I made factory functions to initialise the objects and start their timers. And this is where I'm stuck. No matter what I do, if I call startTimer on the LatchOnSlider object, the function of the parent gets called instead. I've looked through at least five different tutorials and tried all their syntax variations, but nothing fixes the problem. I've walked through it in firebug, the process works as expected up to that point. The constructor of LatchOnSlider gets called, the constructor of the base object gets called from there, everything initialises properly, just when LatchOnSlider.startTimer() is called, LatchOn.startTimer() gets executed instead. I'm pretty much out of ideas as to what might be the problem at this point.

This line is the problem:
function NewLatchOnSlider(element_id, container_id, image_class, screenpos) {
LatchOnSlider.prototype = Object.create(LatchOn.prototype);
Here, you are creating a new prototype object every time an instance is created. Those new prototypes do not have the methods you had assigned on the "old" LatchOnSlider.prototype before, and your instances will not inherit them - only the LatchOn.prototype methods indirectly.
You will need to keep the constructor declaration and the prototype initialisiation separate from your factory function:
function NewLatchOnSlider(element_id, container_id, image_class, screenpos) {
var newSlider = new LatchOnSlider(element_id, container_id, image_class, screenpos);
newSlider.startTimer();
return newSlider;
}
function LatchOnSlider(element_id, container_id, image_class, screenpos) {
LatchOn.call(this, element_id, container_id, screenpos, "CONTAINER");
// initialise own stuff
}
LatchOnSlider.prototype = Object.create(LatchOn.prototype);
LatchOnSlider.prototype.constructor = LatchOnSlider;
LatchOnSlider.prototype.startTimer = function() { … }
LatchOnSlider.prototype.…
For assignments, order matters :-)

Related

javascript namespace call other function methods

I try to change some way to call methods into namespace.
Calling parent methods (I dont think its possible)
Creating and call inheritance function
Calling inside another method (mostly jquery onReady event function) (this.MyFunction() not working)
I split every namespace in files (want to keep it that way)
I try How to call function A from function B within the same namespace? but I didn't succed to split namespaces.
my fiddle sample got only 1 sub-namespace but could be more.
https://jsfiddle.net/forX/kv1w2rvc/
/**************************************************************************
// FILE Master.js
***************************************************************************/
if (!Master) var Master = {};
Master.Print= function(text){
console.log("master.Print :" + text);
$("body").append("<div>master.Print : " + text + "</div>");
}
/**************************************************************************
// FILE Master.Test1.js
***************************************************************************/
if (!Master) var Master = {};
if (!Master.Test1) Master.Test1 = {};
/**************************************************************************
* Descrition :
* Function for managing event load/documentReady
**************************************************************************/
Master.Test1.onReady = function () {
$(function () {
Master.Test1.Function1(); //try to replace because need all namespace.
try {
this.Function2(); //not working
}
catch(err) {
console.log("this.Function2 not working");
$("body").append("<div>this.Function2 not working</div>");
}
try {
this.Print("onReady"); //not working
}
catch(err) {
console.log("this.Print not working");
$("body").append("<div>this.Print not working</div>");
}
try {
Print("onReady"); //not working
}
catch(err) {
console.log("Print not working");
$("body").append("<div>Print not working</div>");
}
});
}
Master.Test1.Function1 = function () {
console.log("Function1");
$("body").append("<div>Function1</div>");
this.Function3(); //working because not inside another function
}
Master.Test1.Function2 = function () {
$("body").append("<div>Function2</div>");
console.log("Function2");
}
Master.Test1.Function3 = function () {
$("body").append("<div>Function3</div>");
console.log("Function3");
Master.Print("Function3"); //try to replace because need all namespace.
}
Master.Test1.onReady();
I use Master.Test1.Function1(); and I want to change that because Function1 is inside the same namespace.
I use Master.Print("Function3"); I dont think I can change that. the way I try to use it, it's more an inheritance function. but I dont know if theres a way to do that?
Maybe I should change the my namespace methode? maybe prototype will do what I want?
You can capture the this in a variable because this inside $(function() {}) will point to document object. The below will work provided you never change the calling context of onReady -- i.e. it is always called on the Test1 object and not called on other context:
Master.Test1.onReady = function () {
var self = this;
$(function () {
self.Function1();
// ..
});
}
To access Print you have to reference using the Master object like: Master.Print() as it won't be available in the Test1 object
this is document within .ready() or jQuery() alias for .ready() where function(){} is parameter $(function() {}). this at this.Function2() will reference document.
"Objects" in javascript are not built the same way as in most object-oriented languages. Essentially, what you are building is a hierarchy of static methods that have no real internal state in-and-of themselves. Therefore, when one of the defined methods is invoked, the context (or state) of that method depends on what object invoked the method.
If you want to have any internal context, you will need to create an "instance" of an "object prototype". At that point, you can use "this.otherFunction" within your other functions. Here is a small example:
var MyObject = function() {};
MyObject.functionOne = function() {
console.log("Function 1");
this.functionTwo();
};
MyObject.functionTwo = function() {
console.log("Function 2");
};
var instanceOne = new MyObject();
instanceOne.functionOne();
You might get some more information about object definition here

Javascript - Call parent function and extend method

This is probably a stupid question, but is there way in Javascript (ES5 preferred) to "extend" a class function similar to how i can i extend a parent' function in PHP ?
Basicly, i have this class hierarchy from System -> Weapon -> Dual and i would like Dual to use the code from System.setState() and then do some more stuff.
Note i use pre ES6 syntax for my hierarchy.
function System(system){
this.setState = function(){
//do stuff
}
}
function Weapon(system){
System.call(this, system);
}
Weapon.prototype = Object.create(System.prototype);
function Dual(system){
Weapon.call(this, system);
this.setState = function(){ // this is the problem
System.prototype.setState(); // error - not defined
//Weapon.protoype.setState() doesnt work either
//do more stuff
}
}
Dual.prototype = Object.create(Weapon.prototype);
Because setState is an instance property of System it does not exist on System.proptotype so you can't call it using System.prototype.setState.call. If you want to call it in this case, just create an object from System like so
function Dual(system){
Weapon.call(this, system);
var parent = new System(system);
this.setState = function() {
parent.setState(); // done
}
}
Instance properties are duplicated on each individual object ( they don't share). Whereas, prototype properties will be shared among children( they are not duplicated on child classes). To make all System 's subclasses share setState function, add it to System 's prototype
function System (arg) { ... }
System.prototype.setState = function () {...}
Now in your child classes, you can do
function Dual(system){
Weapon.call(this, system);
this.setState = function() {
System.prototype.setState.call(this); // done
}
}
First, you should set your instance methods on the prototype:
System.prototype.setState = function() {
// your stuff
}
This will improve performance and allow you to inherit the method without constructing a System instance.
Then, you just need to call System's version of setState on the right object (the instance of Dual) instead of calling it on System.prototype:
Dual.prototype = Object.create(Weapon.prototype, {
'setState': { value: function(){
System.prototype.setState.call(this) // fixed
// other stuff
}}
})

Call function within another in Javascript

I've the following script:
gapi.analytics.ready(function() {
viewSelector.on('viewChange', function update (data) {
var title = document.getElementById('view-name');
title.innerHTML = data.property.name + ' (' + data.view.name + ')';
activeUsers.set(data).execute();
renderWeekOverWeekChart(data.ids);
renderTopBrowsersChart(data.ids);
renderTopCountriesChart(data.ids);
setTimeout(function() {
var list = document.getElementsByTagName("tr")[0];
list.getElementsByTagName("th")[0].innerHTML = "Pagina's";
list.getElementsByTagName("th")[1].innerHTML = "Paginaweergaven";
}, 500);
});
});
And within the following code I would like to re-run the update(); function.
function datumwissel( datumbtn ) {
if ( datumbtn.className == 'maand' ) {
datumbtn.className = 'jaar';
dimensions1 = 'ga:month,ga:nthMonth';
start1 = moment(now).date(1).month(0).format('YYYY-MM-DD');
end1 = moment(now).format('YYYY-MM-DD');
start2 = moment(now).subtract(1, 'year').date(1).month(0).format('YYYY-MM-DD');
end2 = moment(now).date(1).month(0).subtract(1, 'day').format('YYYY-MM-DD');
format1 = 'M';
format2 = 'MMM';
update();
}
else {
datumbtn.className = 'maand';
dimensions1 = 'ga:date,ga:nthWeek';
start1 = moment(now).subtract(2, 'day').date(1).format('YYYY-MM-DD');
end1 = moment(now).format('YYYY-MM-DD');
start2 = moment(now).subtract(2, 'day').date(1).subtract(1, 'month').format('YYYY-MM-DD');
end2 = moment(now).subtract(2, 'day').date(1).subtract(1, 'day').format('YYYY-MM-DD');
format1 = 'YYYYMMDD';
format2 = 'Do';
update();
}
}
But somehow this doesn't work. I also tried in the above script:
window.update = function (data) {}. But that also doesn't work.
How can I call the update(); function that is situated inside the gapi.analytics.ready(function() {} ?
Important is that I cannot make it globally as it has to be situated inside the gapi.analytics.ready().
It's really a simple matter of moving the function declaration
function update (data) {
// same as existing code
}
gapi.analytics.ready(function() {
viewSelector.on('viewChange', update );
});
And passing in data needed when you call it in your other function
function datumwissel( datumbtn ) {
if ( datumbtn.className == 'maand' ) {
..........
update(datumbtn);
}.......
Important is that I cannot make it globally as it has to be situated inside the gapi.analytics.ready()
That's not actually true - you can have it global and there at the same time. Whether you want to, is a different manner, as that would pollute the global namespace and so on. However, here is how that can be achieved:
First, extract the update function outside of the ready handler like so
function update (data) {
var title = document.getElementById('view-name');
title.innerHTML = data.property.name + ' (' + data.view.name + ')';
activeUsers.set(data).execute();
renderWeekOverWeekChart(data.ids);
renderTopBrowsersChart(data.ids);
renderTopCountriesChart(data.ids);
setTimeout(function() {
var list = document.getElementsByTagName("tr")[0];
list.getElementsByTagName("th")[0].innerHTML = "Pagina's";
list.getElementsByTagName("th")[1].innerHTML = "Paginaweergaven";
}, 500);
}
This will create a new function with the name update which accepts one parameter called data. Thanks to hoisting it would not matter if it's before or after anywhere you want to use it, as it would be effectively "pulled" to the top.
Next, you can just use the function inside the ready handler like so:
gapi.analytics.ready(function() {
viewSelector.on('viewChange', update);
});
Since .on(events, handler) accepts a function as the second parameter, you can just provide a function reference there. It doesn't matter that your function is technically declared elsewhere, as it is still going to be called with the same arguments. Similarly, if you replace update with alert you will be giving the reference to window.alert so you will get an alert with data.
With that, you can just call the same function in your other piece of code.
That is true for any place that uses callbacks, including setTimeout - you can just give a function reference and it's going to be called. Internally, those kinds of functions almost always do something like callback() or callback(someData), occasionally callback.call(/* parameters */) where callback is the passed in argument. Whether you define that argument as you are calling the function, e.g., selector.on("click", function() {/* code */}) or separately, e.g.,
function clickHandler() { /* code */ }
selector.on("click", clickHandler)
matters little.
With that said, whether you want the function global is a different matter. Unless both pieces of code are in the same place, a global function may be the easiest way. You could, also, namespace anything your app uses, which would partially avoid the global pollution. Not completely, but sometimes you just need to have things living under window if you have multiple files, in which case, you can define your own little corner there to play with: window.myApp = window.myApp || {} would create a new object that can serve as namespace and so you will be able to do things like myApp.update = function(data) { /* code */ } and thus share that code.
If your two pieces of code are indeed in one file, then you merely need to create the function outside both using var update = function(data) { /* code */ } then hand it to each in the exact same way, since update is still going to be a function reference, however, if assigned to a variable, it won't be added to the global namespace (nor would the declaration be hoisted).

JavaScript: TypeError: xyz is not a function when calling the function

I am trying to come up with a page on which, when user clicks a file button on the page, I try to execute the JS on the page. And I am trying to use OOP / class so hopefully it can be reused later. Here is my test code:
// This is the "class".
function BearUpload() {
// some values will go here...
}
// Add a few functions
BearUpload.prototype.function1 = function () {
console.log("function1 called");
}
BearUpload.prototype.handleFileSelect = function (evt) {
console.log("handleFileSelect called");
this.function1();
}
var myBear = new BearUpload(); // Create a global variable for the test
$(document).ready(function () {
var some_condition_goes_here = true;
if (some_condition_goes_here) {
$("#my-file-select-button").change(myBear.handleFileSelect);
}
});
However, it gets error like:
TypeError: this.function1 is not a function
this.function1();
Any idea about this?
Thanks!
Bind myBear to your change eventListener
In general when you access this from handleFileSelect, this refers to the html element.
i.e. this = <input type="file" id="my-file-select-button">
$("#my-file-select-button").change(myBear.handleFileSelect.bind(myBear));
The bind() method creates a new function that, when called, has its
this keyword set to the provided value, with a given sequence of
arguments preceding any provided when the new function is called.
MDN doc
You are trying to call function1 on DOM object but you have to call on jQuery object
$(this).function1();
That's because when bound as a handler to jQuery events, this would refer to the element on which the event is triggered.
I would rather change your code like this
// Create only one global variable for your app
var APP = {};
// Create class using immediate function/closure
APP.BearUpload = (function(){
//declare private variables here
// Constructor
var bearUpload = function() {
// some values will go here...
}
// Add a few functions
bearUpload.prototype.function1 = function () {
console.log("function1 called");
}
bearUpload.prototype.handleFileSelect = function (evt) {
console.log("handleFileSelect called");
this.function1();
}
return bearUpload;
}());
APP.myBear = new APP.BearUpload();
$(document).ready(function () {
var some_condition_goes_here = true;
if (some_condition_goes_here) {
$("#my-file-select-button").change(function(e){
// do something with event 'e'
APP.myBear.handleFileSelect.call(APP.myBear, e);
});
}
});
do not use "this", it is confusing some time.
BearUpload.prototype ={
function1:function(){
var self = this;
...
},
handleFileSelect:function(e){
var self = this;
...
}
}

Javascript Class Inheritance

Can anyone tell me why my 'showDiv_boo' is undefined inside the class´s method?
I also can´t access my class´s methods.
Here´s my class 'Blink' class with its properties and methods:
function Blink(div) {
this.div = div
}
Blink.prototype.counter = 0
Blink.prototype.showDiv_boo = true
Blink.prototype.showDiv = function() {
this.div.style.visibility = 'visible'
}
Blink.prototype.hideDiv = function() {
this.div.style.visibility = 'hidden'
}
Blink.prototype.startEngine = function() {
if (this.showDiv_boo) {
this.showDiv()
} else if (!this.showDiv_boo) {
this.hideDiv()
}
this.showDiv_boo = !this.showDiv_boo
this.counter++
}
Blink.prototype.startEffect = function() {
this.idEffect = setInterval(this.startEngine, 1000 / 45)
}
So, if I create:
_blink = new Blink(myDiv);
_blink.startEffect();
You can test... the variable 'showDiv_boo', is undefined inside the method.
Even, if I set the showDiv_boo inside the method to true, it won´t call my class´s methods showDiv or hideDiv.
Anyone?
Thanks :)
The reason why is that startEngine is called from setInterval. The way in which this callback is invoked causes startEngine to have a different value for this than startEffect. You need to save this in order to maintain it in the callback. For example.
Blink.prototype.startEffect = function () {
var self = this;
self.idEffect = setInterval(function () { self.startEngine(); }, 1000 / 45);
};
You need to:
use var self and call the method via self.startEngine()
use an anonymous function to wrap the call in [1] i.e. function(){ self.startEngine(); }
This is because when you just pass this.startEngine or self.startEngine you are just passing the function startEngine without specifying what this is, which in both cases is supplied by the global conext of DOMWindow.
To give an example...
function startEngine() {
...code omitted...
};
Blink.prototype.startEngine = startEngine;
Blink.prototype.start = function() {
setTimeout(startEngine, 0); // obviously wrong, what is this?
setTimeout(Blink.startEngine, 0); // actually the same as line above, although not as obvious
setTimeout(startEngine.bind(this), 0); // works correctly
}
works to add code to the prototype and if used in the anonymous function will work as expected, but if you just use Blink.startEngine as the callback it is exactly the same as using startEngine only the second is more obviously wrong because there's no object it is being called on so you'd expect this to be whatever is supplied by the context.
The other way you could do this without using the anonymous function would be
Blink.startEngine.bind(self)
Which returns a function that will call startEngine with the correct this same as explicitly creating the anonymous function and wrapping the call to self.startEngine()
Heres a link to a fiddle to play around with the differences: http://jsfiddle.net/bonza_labs/MdeTF/
If you do the following, you will find it is defined
var x = new Blink('hello');
x.showDiv_boo
Javascript uses prototypical inheritance. While showDiv_boo may not be explicitly defined within the instance of Blink that you now have, it does exist within the prototype that Blink inherits from. When you try referencing showDiv_boo from within the object, the Javascript engine realizes the object does not own a member by that name and then will check its prototype.
Along with setting a temporal variable to store this, you must call the startEngine() function with that variable:
Blink.prototype.startEffect = function(){
var self = this;
self.idEffect = setInterval(function(){ self.startEngine.call(self); }, 1000/45);
}
Note the .call(self), which basically calls the function with the variable self, so the variable this in startEngine will be the correct one.

Categories