Calling function inside javascript class functions? - javascript

I am trying to declare properties and functions to a specific js class. But when I try to call function inside a function (even if called after declared) it throws a exception
Uncaught TypeError: Object [object Object] has no method 'addButtonEventListeners
My code listed: SplashScene.js
var anim1, background;
var SplashScene;
function SplashScreen(SceneContainer)
{
this.name = "SplashScreen";
this.loadScreen = function()
{
background = new createjs.Shape();
background.graphics.beginBitmapFill(loader.getResult("gameLoopSplash")).drawRect(0,0,w,h);
SplashScene = new createjs.Container();
anim1 = new createjs.Sprite(buttonData, "playIdle");
anim1.framerate = 30;
anim1.x = 260;
anim1.y = 22;
SplashScene.alpha = 0;
SplashScene.addChild(background);
SplashScene.addChild(anim1);
}
this.addButtonEventListeners = function()
{
console.log("addButtonEventListeners SplashScreen" );
}
this.menuIn = function()
{
console.log("menuIn SplashScreen" );
stage.addChild(SplashScene);
var tween = createjs.Tween.get(SplashScene).to({y : 0, x : 0, alpha : 1}, 5000).call(this.menuInCompleted);
var splashTimer = window.setTimeout(function(){menuOut("MainScreen", false)},15000);
}
this.menuInCompleted = function()
{
console.log("menuInCompleted SplashScreen" );
this.addButtonEventListeners();
}
}
Can anybody tell me how can I do this?

The problem is that the context (this) in the setTimeout callback is window, not your object.
You can change
var splashTimer = window.setTimeout(function(){menuOut("MainScreen", false)},15000);
to
var splashTimer = window.setTimeout(
(function(){menuOut("MainScreen", false)}).bind(this)
,15000);
and you also have to do the same where you bind menuIn (to an event?).

Your problem is this, which points to the current context (in runtime) and not to your object's one.
Just add a variable in SplashScreen like:
var self=this; //keeping the context of SplashScreen
And then call it as follows:
this.menuInCompleted = function()
{
console.log("menuInCompleted SplashScreen" );
self.addButtonEventListeners ();
}

Related

Equivalent of interfaces in javascript

I have a function object in javascript called BlinkyTextBox Inside of that I have 2 Shape objects that act as scroll buttons. I need a something very simple to happen which is just increment or decrement a variable called scrollY.
I tried it with an anonymous inner function, but the function couldn't recognize the member variables. Now I tried it with a member function, but it doesn't work with that either...
Here are both samples of what I am talking about.
function BlinkyTextBox(textdata, font, w, h)
{
this.scrollY = -50;
this.scrollBarYTop = new Button();
this.scrollBarYTop.callFunction = this.scrollUp;
this.scrollBarYBottom = new Button();
this.scrollBarYBottom.callFunction = function()
{
this.scrollY -= 10;
}
}
BlinkyTextBox.prototype.scrollUp = function()
{
this.scrollY += 10;
}
The problem here is that once you assign a function to another object the this inside that function will refer to the new object instead of the object the function came from.
For example:
var a = {
message : 'Hello!',
say : function () { return this.message }
}
var b = {
message : 'Goodbye'
}
b.say = a.say;
console.log(a.say()); // Hello!
console.log(b.say()); // Goodbye
Notice that we didn't do anything to the function say(). We just assigned it to b and it now print's the message from b instead of a.
Now, let's look at your code:
this.scrollBarYBottom.callFunction = function()
{
this.scrollY -= 10; // refers to scrollBarYBottom.scrollY
// not BlinkyTextBox.scrollY
}
Same thing happens to the other method:
this.scrollBarYTop.callFunction = this.scrollUp;
// the this inside scrollUp will now refer to scrollBarYTop
Traditionally, to fix this you'd use an alias for this:
var myself = this;
this.scrollBarYBottom.callFunction = function()
{
myself.scrollY -= 10;
}
But with ES5 you can use the .bind() method:
this.scrollBarYBottom.callFunction = (function()
{
this.scrollY -= 10;
}).bind(this);
and:
this.scrollBarYTop.callFunction = this.scrollUp.bind(this);
Refer to this answer for a more detailed explanation of this: How does the "this" keyword in Javascript act within an object literal?

Function inside an object gets wrong context

I have a constructor:
var ProfileDialog = function (containerObj) {
this.init = function () {
this.container = containerObj;
let content = document.createElement('div');
content.innerText = 'Dialog here';
this.container.appendChild(content);
this.container.style.display = 'none';
};
this.init();
};
Then I am extending the prototype of the constructor like this:
ProfileDialog.prototype.open = function () {
this.container.style.display = 'block';
}
var dlg = new ProfileDialog(document.body)
dlg.open();
This works fine, but if I try to put .open() inside an object like this:
ProfileDialog.prototype.actions = {
open: function () {
this.container.style.display = 'block';
}
}
var dlg = new ProfileDialog(document.body)
dlg.actions.open();
It fails with an error
functional.js:25 Uncaught TypeError: Cannot read property 'style' of undefined(…)
because of the wrong context passed to the function.
How do I make sure that independently of the nesting, context would be always the instantiated object?
Can you try binding 'this' to actions.open
Maybe you save this to that before and take it as a closure for the init function.
var ProfileDialog = function (containerObj) {
var that = this;
this.init = function () {
that.container = containerObj;
let content = document.createElement('div');
content.innerText = 'Dialog here';
that.container.appendChild(content);
that.container.style.display = 'none';
};
this.init();
};

JS: Add a function with "prototype" not possible

I really can't find the problem. The Browser always gives the following error:
TypeError: undefined is not a function (evaluating 'this._createButtons()')
This is my code:
function OpenSlider(el, steps) {
this.ul = el;
this.steps = steps;
this.slider_width = $(this.ul).width();
this.content_width = this.ul.scrollWidth;
this._createButtons();
}
OpenSlider.prototype = {
_createButtons : function() {
alert("created");
}
}
And in another document:
var slider = OpenSlider(document.getElementById("element-id"),1);
Maybe you could help me...
You need to new up the object with the OpenSlider constructor function:
var slider = new OpenSlider(document.getElementById("element-id"),1);
also change the line to:
OpenSlider.prototype._createButtons = function() {
alert("created");
};
as you are overwriting the prototype object instead of extending it with an additional function.

Reference error "object property is not defined"

I have the following structure:
appInterface = {
mainWinCanvas: document.getElementById("mainwindow"),
mainWinContext: mainWinCanvas.getContext("2d"),
mainWinCanvasWidth: mainWinCanvas.width,
mainWinCanvasHeight: mainWinCanvas.height,
mainWinCanvasData: mainWinContext.getImageData(0, 0, mainWinCanvasWidth, mainWinCanvasHeight)
}
and get this error in Firebug:
mainWinCanvas is not defined
What's causing it? I'm sure the script is called AFTER body element previous children are fully loaded.
My goal is to make the code more readable, it's no-object version is working :(
All you have to do is wrap this in a function and return it as object so the this context should be available to your current appInterface Object. Also convert your properties to methods, so you can able to do method chaining.
var appInterface = function () {
return {
canvas: null,
ctx: null,
mainWinCanvas: function (elem) {
if (this.canvas === null) {
this.canvas = document.getElementById(elem);
}
return this;
},
mainWinContext: function () {
this.ctx = this.canvas.getContext("2d");
return this;
},
mainWinCanvasWidth: function () {
return this.canvas.width;
},
mainWinCanvasHeight: function () {
return this.canvas.height;
},
mainWinCanvasData: function () {
this.ctx.getImageData(0, 0, this.mainWinCanvasWidth(), this.mainWinCanvasHeight());
return this;
}
};
};
Usage:
appInterface().mainWinCanvas('mainWindow').mainWinContext().mainWinCanvasWidth();
There's not much more coding, when creating an object with a constructor function:
function AppInterface (cnvid) {
this.mainWinCanvas = document.getElementById(cnvid);
this.mainWinContext = this.mainWinCanvas.getContext("2d");
this.mainWinCanvasWidth = this.mainWinCanvas.width;
this.mainWinCanvasHeight = this.mainWinCanvas.height;
this.mainWinCanvasData = this.mainWinContext.getImageData(0, 0, this.mainWinCanvasWidth, this.mainWinCanvasHeight);
}
var appInterface = new AppInterface("mainwindow");
You can even reuse the constructor, if you'd need more than one "appInterFace" in your app.
The object has no local context, you need to acces by its main reference appInterface
appInterface = {
mainWinCanvas: document.getElementById("mainwindow"),
mainWinContext: appInterface.mainWinCanvas.getContext("2d"),
mainWinCanvasWidth: appInterface.mainWinCanvas.width,
mainWinCanvasHeight: appInterface.mainWinCanvas.height,
mainWinCanvasData: appInterface.mainWinContext.getImageData(0, 0, appInterface.mainWinCanvasWidth, appInterface.mainWinCanvasHeight)
}
If you want to have local context use functions instead
EDIT
use function constructor instead, you need a live instance for self referencing
var appInterface = new function(){
this.test = 4;
};
appInterface = {
anotherTest:appInterface.test
}
console.log(appInterface.test)
One lame workaround to escape writting functions and getters/setters is to do the following:
appInterface = new Object();
appInerface.mainWinCanvas = document.getElementById("mainwindow");
appInerface.mainWinContext = appInerface.mainWinCanvas.getContext("2d");
...
This is stupid, i'm not deeply in JS but don't see the difference between new Object() and corresponging defining of its properties, or structure in the question ...

Javascript inheritance problem when using prototypes - instances overwritten :(

I'm new to JavaScript programming and I am having a bit of a nightmare with inheritance. I am writing some code for Appcelerator Titanium and I have a base class called Slide2D that I wish to inherit from.
So I have placed a few functions in Slide2D's prototype. These are generally not going to be overwritten, but will be called from classes derived from Slide2D. These functions will also be called from other parts of the program. There are also various eventhandlers used to manage animation in Titanium.
If I make a couple of these slides in some calling code (using new)
var s = new Slide2D('slide1', 'background1.png', etc......
var t = new Slide2D('slide2', 'background2.png', etc......
all of my prototype methods point to the last Slide2D created, regardless of whether I use s or t. So 'slide2' will always be displayed, even if I'm using the s variable.
This is driving me mad - any help would be greatly appreciated.
Sorry for the length of the code but here it is:
function Slide2D(name, backgroundImage, transform, inAnimation, outAnimation)
{
Titanium.API.info('Slide2D - Constructor - ' + name);
var _self = this;
var _name = name;
var _backgroundImage = backgroundImage;
var _startingTransform = transform;
var _slideView = Titanium.UI.createView({
backgroundImage: _backgroundImage,
transform: transform
});
var _animateInAnimation = Titanium.UI.createAnimation();
_animateInAnimation.transform = Titanium.UI.create2DMatrix().translate(0,0);
_animateInAnimation.duration = 750;
var _animateOutAnimation = Titanium.UI.createAnimation();
_animateOutAnimation.transform = Titanium.UI.create2DMatrix().translate(-1024,0);
_animateOutAnimation.duration = 750;
var onAnimateInStart = function()
{
Titanium.API.info('Slide2D.onAnimateInStart');
Titanium.App.fireEvent('animateInStart', {slideName: _name});
_animateInAnimation.removeEventListener('start', onAnimateInStart);
};
var onAnimateOutStart = function()
{
Titanium.API.info('Slide2D.onAnimateOutStart');
Titanium.App.fireEvent('animateOutStart', {slideName: _name});
_animateInAnimation.removeEventListener('start', onAnimateOutStart);
};
var onAnimateInComplete = function()
{
Titanium.API.info('Slide2D.onAnimateInComplete');
Titanium.App.fireEvent('animateInComplete', {slideName: _name});
_animateInAnimation.removeEventListener('complete', onAnimateInComplete);
};
var onAnimateOutComplete = function()
{
Titanium.API.info('Slide2D.onAnimateOutComplete');
Titanium.App.fireEvent('animateOutComplete', {slideName: _name});
_animateOutAnimation.removeEventListener('complete', onAnimateOutComplete);
};
_animateInAnimation.addEventListener('start', onAnimateInStart);
_animateOutAnimation.addEventListener('start', onAnimateOutStart);
_animateInAnimation.addEventListener('complete',onAnimateInComplete);
_animateOutAnimation.addEventListener('complete', onAnimateOutComplete);
Slide2D.prototype.animateIn = function(){
Titanium.API.info('Slide2D.prototype.animateIn - ' + _name);
_slideView.animate(_animateInAnimation);
};
Slide2D.prototype.animateOut = function(){
Titanium.API.info('Slide2D.prototype.animateOut');
_slideView.animate(_animateOutAnimation);
};
Slide2D.prototype.getName = function()
{
return _name;
};
Slide2D.prototype.getView = function(){
Titanium.API.info('Slide2D.prototype.getView');
return _slideView;
};
Slide2D.prototype.getStartingTransform = function(){
return _startingTransform;
};
};
Edit
Thanks very much for your prompt reply. I have made the changes that you recommended and that has resolved that particular problem.
However, a new issue has been introduced.
I need to call Slide2D.prototype.getView from a derived class - ExtendedSlide2D.
However, now I get the following error :
Result of expression 'Slide2D.prototype.getView()' [undefined] is not an object
at ExtendedSlide2D.js at line .......
This is where I add the button to the base class's view object.
I'm sure this error just comes down to my inexperience with the language but, once again, any help would be greatly appreciated.
Once again - here is the code for ExtendedSlide2D:
Titanium.include('Slide2D.js');
function ExtendedSlide2D(name, backgroundImage, transform, inAnimation, outAnimation)
{
Titanium.API.info('ExtendedSlide2D - Constructor');
this.base = new Slide2D(name,
backgroundImage,
transform,
inAnimation,
outAnimation);
ExtendedSlide2D.prototype.constructor = ExtendedSlide2D;
var button = Titanium.UI.createButton({title: 'AnimateOut',
height: 40,
width: 200,
top: 50
});
button.addEventListener('click', function()
{
Slide2D.prototype.animateOut();
});
Slide2D.prototype.getView().add(button);
}
ExtendedSlide2D.prototype = new Slide2D();
You'll need to move the methods that you adding to the prototype outside of the Slide2D constructor function. That way they only get defined once instead of with every object instantiation.
And then for those prototype functions to access the "internals", such as _name, _slideView, etc... You will need to convert all your "vars" (currently accessible under closure) to be properties of the object itself. Then reference all those member properties with a "this." prefix.
Below, I brute-forced replaced all your "vars" to be this.properties. Only the properties and function you need to access from the prototype methods need this conversion. For internal functions (such as your animate functions), they may still be able to use vars accessible under closure.
function Slide2D(name, backgroundImage, transform, inAnimation, outAnimation) {
Titanium.API.info('Slide2D - Constructor - ' + name);
_self = this;
this._name = name;
this._backgroundImage = backgroundImage;
this._startingTransform = transform;
this._slideView = Titanium.UI.createView({
backgroundImage: this._backgroundImage,
transform: transform
});
this._animateInAnimation = Titanium.UI.createAnimation();
this._animateInAnimation.transform = Titanium.UI.create2DMatrix().translate(0, 0);
this._animateInAnimation.duration = 750;
/// ...
};
Slide2D.prototype.animateIn = function () {
Titanium.API.info('Slide2D.prototype.animateIn - ' + this._name);
this._slideView.animate(this._animateInAnimation);
};
Slide2D.prototype.animateOut = function () {
Titanium.API.info('Slide2D.prototype.animateOut');
this._slideView.animate(this._animateOutAnimation);
};
Slide2D.prototype.getName = function () {
this._name;
};
Slide2D.prototype.getView = function () {
Titanium.API.info('Slide2D.prototype.getView');
this._slideView;
};
Slide2D.prototype.getStartingTransform = function () {
this._startingTransform;
};

Categories

Resources