Carrying scope of "this" into inner functions - javascript

How can I extend the scope of this inward? I thought reassigning a higher scoped variable would do the trick... what am I doing wrong? http://jsfiddle.net/8bqXM/
function Player(configs) {
this.opts = {
playerID: "cSurf"
};
configs = $.extend(this.opts, configs);
var the = this;
this.init = function(){
var $player = $("#" + the.configs.playerID);
alert($player.attr("id"));
}
}
var cSurf = new Player();
$(document).ready(function(){
cSurf.init();
});

In your code, configs is not a public member of the Player object being created. You declared it without var so it's a global variable.
configs = $.extend(this.opts, configs);
should be:
this.configs = $.extend(this.opts, configs);
Then in your init() you can do the.configs.playerID.
Example: http://jsfiddle.net/rCuXa/
(Your jsFiddle also had MooTools loaded instead of jQuery. Check the menu on the left.)
EDIT:
By the way, since you're calling init() from the context of the new Player object, you really don't need to use the as the reference to this.
Inside init method, this refers to the Player that was created when you call it that way.
var $player = $("#" + this.configs.playerID);
Example: http://jsfiddle.net/rCuXa/1/
EDIT2: Additionally, if init() is always going to be called from the context of the Player instance you create, you may want to consider placing it on the prototype object of Player.
That way it will be shared among all instances instead of being recreated for each one:
Player.prototype.init = function(){
// ---------v----------"this" is the instance of Player
var $player = $("#" + this.configs.playerID);
alert($player.attr("id"));
}
Example: http://jsfiddle.net/rCuXa/2/

You just have a minor bug here. You're assigning the extend to configs, which is not on this, but then you reference it later via 'the' as if it is. This works:
function Player(configs) {
this.opts = {
playerID: "cSurf"
};
this.configs = $.extend(this.opts, configs);
var the = this;
this.init = function(){
var $player = $("#" + the.configs.playerID);
alert($player.attr("id"));
}
}
var cSurf = new Player();
cSurf.init();

Related

confusion, this-pointer and events in javascript

I have a javascript object which I would like to be able to handle some interactive features. It's a bit tricky to describe the scenario in a simple way so hopefully it'll not get all out of hand here.
my object looks something like
myobject = function() {
this.initialize = function() {
// HERE this = the myobject instance
var test = document.createElement('div');
test.onmousedown = this.mousedown;
}
this.mousedown = function(e) {
// HERE this = the calling div-element
}
}
So my problem is basically that this will not be an myobject instance when this.mousedown(e) is called, it will rather be the caller (if the terminology is correct?) in this case it is the div I created and put in a variable called test above.
I would like to have access to the instance on which the method is being run (as I believe that to be the mousedown method I created).
This far I have had some ideas which I have tried out:
create a data- attribute on the div containing the this object and operate on that.
sending the this pointer as an argument along with e to this.mousedown(e)
It's all I can think of now hope it makes sence.
You could create a copy when you first instantiate the object:
var myobject = function() {
var self = this;
this.initialize() {
// HERE this = the myobject instance
var test = document.createElement('div');
test.onmousedown = this.mousedown;
}
this.mousedown(e) {
// HERE this = the calling div-element
// use self instead of this
}
}
The simplest solution is to make a 'self' var that you refer to in the callback:
myobject = funciton() {
var self = this;
this.initialize() {
//use self to refer to myobject
self.mousedown(e);
}
this.mousedown(e) {
}
}

"this" in JavaScript. reference to an object inside a factory

I wrote some classes in javascript and i wrote a few FunctionFactories for them. But I think that i have done some things wrong.
I renamed some things of my code, that you can understand it better.
So the first class is the "root"-class. this class has children, which i add later.
function templateRoot(){
this.id = "root";
this.parent = null;
this.children = [];
this.editable = true; // bla
this.render = function(){
$.each(this.children,function(i,obj){
this.children[i].render();
var baseButtons = this.getBaseButtons();
$('#'+this.id).append(baseButtons);
});
};
this.addBase = addBaseFactory(this);
};
The attribute "addBase" gets a function which is delivered by addBaseFactory...
function addBaseFactory(that){
return function(){
var newBase = new base(that.children.length, that.id);
that.children.push(newBase);
};
}
...and the base class which is used to generate a object in "addBase" looks like this:
function base(count, parent){
this.id = parent+"_base"+count;
this.parent = parent;
this.children = [];
this.remove = function (){
$('#'+this.id).remove();
};
this.render = baseRenderFactory(this);
this.addChild = addChildFactory(this);
this.getBaseButtons = function(){
var addAttributeButton = new $("<button>+ Attribute</button>").button();
var addTextButton = new $("<button>+ Text</button>").button();
return [addAttributeButton, addTextButton];
};
}
The problem now is. When i debug the code and set a breakpoint within the "render" function of the root-object. Then i can see, that "this" is not the root but the "base" object. And i cannot figure out why it is like that because the "root" object is the owner of this function, and my base has an own render function which is not called directly there.
So even the "this" in the line
$.each(this.children,function(i,obj){
Refers to the "base" object. But the "this" is inside the "root" object...
Hope you can help me :-)
EDIT:
The code to let it run:
var test = new templateRoot();
test.addBase();
test.render();
EDIT 2:
"that" in "addBaseFactory" refers to the correct "base" object.
I found your explanation pretty confusing, so I may have misinterpreted what you're trying to do, but I think you expect this within your nested functions to the same object as the this in the outer templateRoot() function. That's not how this works in JavaScript. Nested functions don't inherit the same this as the containing function - each function has its own this object that is set depending on how the function is called.
Here's one possible solution, which uses the fact that nested functions can see variables from their containing function(s):
function templateRoot(){
var self = this; // save a reference to this for use in nested functions
this.id = "root";
this.parent = null;
this.children = [];
this.editable = true; // bla
this.render = function(){
$.each(self.children,function(i,obj){
self.children[i].render();
var baseButtons = this.getBaseButtons();
$('#'+self.id).append(baseButtons);
});
};
this.addBase = addBaseFactory(this);
};
A detailed explanation about how this works in JS can be found at MDN.
Wouldn't this render its childerens children, since jquery would send each child as this?
this.render = function(){
$.each(this.children,function(i,obj){
this.children[i].render();
var baseButtons = this.getBaseButtons();
$('#'+this.id).append(baseButtons);
});
};
Btw in what scope is addBaseFactory called? Because I think the "this" in the base, will refer to that scope.

Basic javascript code layout

I have what I think is a fairly simply question but it's one that I can not find the answer to. I have a objects literal that I have created that groups functions, I want to know how I can create a variable that is inside the objects literal and editable/accessable by all the functions within that objects literal. At the moment the only way I know how to do this is create a global variable but I want to stop populating the global in this way. To better describe what I'm looking fiddle
http://jsfiddle.net/aT3J6/
Thanks, for any help.
var clickCount = 0;
/* I would like to place clickCount inside hideShowFn Object but all function inside need access to it, so global within hideShowFn */
hideShowFn = {
init:function(){
$('.clickMe').click(this.addToCount);
},
addToCount:function(){
clickCount++;
$('<p>'+ clickCount + '</p>').appendTo('body');
}
}
hideShowFn.init();
Create a function which is invoked immediately and returns the object, with the private variable inside the function, like this:
var obj = (function () {
var privateStuff = 'private';
return {
func1: function () {
//do stuff with private variable
},
func2: function () {
//do stuff with private variable
}
};
}());
http://jsfiddle.net/BE3WZ/
This is the way to have private variables in Functional Programming.
http://jsfiddle.net/mattblancarte/aT3J6/10/
Another option would be the pseudo-classical style:
function Constructor(){
var private = 'private';
this.public = 'public';
this.methods = {
//your methods here...
};
}
var obj = new Constructor();
Don't forget to use the 'new' keyword, or else you are going to be globally scoped.
Your code translated to this style would be:
function Test(){
var that = this,
clickCount = 0;
this.init = function(){
$('.clickMe').click(this.addToCount);
};
this.addToCount = function(){
clickCount++;
$('<p>'+ clickCount + '</p>').appendTo('body');
};
}
var test = new Test();
test.init();
You can make a closure as Cokegod says or you can simply add the variable to the object and access it using this
hideShowFn = {
clickCount: 0,
init:function(){
$('.clickMe').click(this.addToCount);
},
addToCount:function(){
this.clickCount++;
$('<p>'+ this.clickCount + '</p>').appendTo('body');
}
}
hideShowFn.init();
This dosn't work as Musa says the scope in addToCount will be the dom node clicked.
But see Cokegod's answer.

Dynamic function call to a class's function, using settimeout

I have a class like structure in javascript, Im attempting to invoke a sibling function using a passed in function name.
This is difficulty to explain so let me show you an example of what im trying to accomplish..
function windowFactory(){
this.init = function(functionName,args[]){
SetTimeout(functionName(args),2000)
}
this.func1 = function(var1){
alert(var1);
}
this.func2 = function(var1, var2){
alert(var1+var2);
}
}
var win1 = new windowFactory();
win1.init("func1","hello");
var win2 = new windowFactory();
win2.init("func2","world","!");
Please note that this is only a demo function, syntax errors / typos included.
Now i had this working using a dreaded Eval when it was outside the class...
eval(funcName+"('"+darray[1]+"','"+darray[2]+"')");
It just required it being outside the Class and passed in dummy values for parameters
Something like this should do the trick:
var windowFactory = function() {
var self = this;
this.init = function(functionName){
var args = Array.prototype.slice.call(arguments, 1);
setTimeout(function() {
self[functionName].apply(self, args);
}, 2000);
};
this.func1 = function(var1){
alert(var1);
};
this.func2 = function(var1, var2){
alert(var1+var2);
};
};
var win1 = new windowFactory();
win1.init("func1","hello");
var win2 = new windowFactory();
win2.init("func2","world","!");
Note the custom self reference var self = this;. This is used because when the timed out function is called, the this object will be window (at least in a web browser).
Another clarification: To address a specific object property in JavaScript you can do in the following ways:
object.property; // Or
object['property']; // When you have a string literal, like in your example

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