Wrong (most probably) THIS context - javascript

I have problem with (most probably) the context of this:
Im new in JS and think more like c++ guy.
Please, see the code:
controller.js :
function CController(){ ...
this.myCanvas = new CCanvas(this);
}
CController.prototype.resize() {...}
canvas.js :
function CCanvas(_mainController){
var controller = _mainController;
}
CCanvas.prototype.myEvent(){
this.controller.resize(); // <--- here!
}
I get the error at mentioned line that controller is undefined.
how can it be corrected?

Most likely not a scoping issue. Assuming your _mainController is actually a controller (which I'll add in the version I'm about to show you), your problem is that in the constructor of CCanvas, you're assigning var controller, not this.controller. This in turn causes controller to be dropped (as var is the keyword for a local variable, after all.
function CCanvas(_mainController){
if (!(_mainController instanceof CController)) throw "Not a controller";
this.controller = _mainController;
}
This should work. And it prevents you from supplying a non-controller.
If you really want to stick to your guns as in the comments and having the variable not on the class but still in lexical scope, do this:
var CCanvas = function(_mainController) {
var outputCCanvas = function(){
};
outputCCanvas.prototype.myEvent = function(){
console.log("Event");
}
return outputCCanvas;
};
The level of indirection on this one is crazy, and you lose a ton of good stuff doing it:
It'll be impossible to do instanceof checks on CCanvas, as each and every instance is generated dynamically every time you call the method
Oh, yeah, your instantiation changes. Now, you're doing new (CCanvas(_mainController))(), since CCanvas is now a method returning a class

Related

Why can this javascript function not access a variable in the same class?

For some reason, the player_controller instance variable created on the asterisked line cannot be called in the update function below. When I run this code, the second asterisked line (the console.log) will recognize and print the specified variable from player_controller to the console. However, when I try to use player_controller in the update function below (the double asterisked line), I get the error: "TypeError: this.player_controller is undefined."
Also note that if the bold line is commented out, the other lines (which have similarly created variables) run without errors.
Can anyone tell me what's going on here?
function Engine(){
var GRAVITY = 0.3;
this.map_loader = new MapLoader();
*this.player_controller = new PlayerController();*
this.map = this.map_loader.load_next_map();
*console.log(this.player_controller.keys_down.A);*
this.update = function(){
**this.player_controller.handle_input(this.map.player);**
gravity(this.map.player);
this.map.player.x += this.map.player.dx;
this.map.player.y += this.map.player.dy;
...
...
edit: Didn't realize I couldn't do formatting in code blocks.
Here is a link to the app, if you view the web console while on this page you'll see the error I'm talking about piling up. http://lukescode.net/senior_project_game/main.html
Possibly you create engine as
var engine = Engine();
instead of
var engine = new Engine();
The problem ended up being that I was making the call to Engine.update with setInterval(engine.update, 1000/60) which, as bfavaretto mentioned in a comment, changes 'this' to be something other than the engine in the update function. The solution was to call the function like this:
setInterval(function(){return engine.update();}, 1000/60)
which causes 'this' to properly refer to the object the update function belongs to instead of the window. Thanks to bfavaretto for pointing me in the right direction.

Overriding methods using Javascript module pattern

I've got a browser addon I've been maintaining for 5 years, and I'd like to share some common code between the Firefox and Chrome versions.
I decided to go with the Javascript Module Pattern, and I'm running into a problem with, for example, loading browser-specific preferences, saving data, and other browser-dependent stuff.
What I'd like to do is have the shared code reference virtual, overrideable methods that could be implemented in the derived, browser-specific submodules.
Here's a quick example of what I've got so far, that I've tried in the Firebug console, using the Tight Augmentation method from the article I referenced:
var core = (function(core)
{
// PRIVATE METHODS
var over = function(){ return "core"; };
var foo = function() {
console.log(over());
};
// PUBLIC METHODS
core.over = over;
core.foo = foo;
return core;
}(core = core || {}));
var ff_specific = (function(base)
{
var old_over = base.over;
base.over = function() { return "ff_specific"; };
return base;
}(core));
core.foo();
ff_specific.foo();
Unfortunately, both calls to foo() seem to print "core", so I think I've got a fundamental misunderstanding of something.
Essentially, I'm wanting to be able to call:
get_preference(key)
set_preference(key, value)
load_data(key)
save_data(key, value)
and have each browser do their own thing. Is this possible? Is there a better way to do it?
In javascript functions have "lexical scope". This means that functions create their environment - scope when they are defined, not when they are executed. That's why you can't substitute "over" function later:
var over = function(){ return "core"; };
var foo = function() {
console.log(over());
};
//this closure over "over" function cannot be changed later
Furthermore you are "saying" that "over" should be private method of "core" and "ff_specific" should somehow extend "core" and change it (in this case the private method which is not intended to be overridden by design)
you never override your call to foo in the ff_specific code, and it refers directly to the private function over() (which never gets overridden), not to the function core.over() (which does).
The way to solve it based on your use case is to change the call to over() to be a call to core.over().
That said, you're really confusing yourself by reusing the names of things so much, imo. Maybe that's just for the example code. I'm also not convinced that you need to pass in core to the base function (just to the children).
Thanks for your help. I'd forgotten I couldn't reassign closures after they were defined. I did figure out a solution.
Part of the problem was just blindly following the example code from the article, which meant that the anonymous function to build the module was being called immediately (the reusing of names Paul mentioned). Not being able to reassign closures, even ones that I specifically made public, meant I couldn't even later pass it an object that would have its own methods, then check for them.
Here's what I wound up doing, and appears to work very well:
var ff_prefs = (function(ff_prefs)
{
ff_prefs.foo = function() { return "ff_prefs browser specific"; };
return ff_prefs;
}({}));
var chrome_prefs = (function(chrome_prefs)
{
chrome_prefs.foo = function() { return "chrome_prefs browser specific"; };
return chrome_prefs;
}({}));
var test_module = function(extern)
{
var test_module = {};
var talk = function() {
if(extern.foo)
{
console.log(extern.foo());
}
else
{
console.log("No external function!");
}
};
test_module.talk = talk;
return test_module;
};
var test_module_ff = new test_module(ff_prefs);
var test_module_chrome = new test_module(chrome_prefs);
var test_module_none = new test_module({});
test_module_ff.talk();
test_module_chrome.talk();
test_module_none.talk();
Before, it was running itself, then when the extension started, it would call an init() function, which it can still do. It's just no longer an anonymous function.

JavaScript design pattern

can someone please explain to me the following JavaScript design pattern example and what it's trying to accomplish?
var Knockback = { };
Knockback.Observables = (function () {
function Observables(model, mappings_info, view_model) {
this.model = model;
this.mappings_info = mappings_info;
this.view_model = view_model;
//logic in here
}
Observables.prototype.destroy = function () {
//logic in here
this.view_model = null;
this.mappings_info = null;
return this.model = null;
};
return Observables;
})();
Knockback.observables = function(model, mappings_info, view_model, options) {
return new Knockback.Observables(model, mappings_info, view_model, options);
};
Knockback is a namespace. Values are stored inside Knockback so they do not clash with any global variables.
Observables is a constructor sitting inside Knockback. All of the logic is inside a closure ((function () {})()) for modularity
observales is used as a method of returning an instance of Observables, This is a way that people can use whats known as "scope safe constructors". In javascript if you call a constructor without new, then the this object defaults to the window, polluting your global namespace again.
I'm not sure how much you know about javascript, but I hope this helps.
-------------------------------- updated --------------
1) The closure functions the same as without a closure, that is correct (At the time of my answer i didnt know that there were no "private" variables). But this pattern also allows you to place this constructor wherever you please. Imagine if the namespace (Knockback) name changed to KB. You could place the constructor there without even needed to change a line of code inside the closure.
2) The Knockback.observer function may be a bloat (which i personally dont think it is) but the "scope safe" factor is considered a best practise. consider:
var standardCorrectInvokation = new Knockback.Observer('model', 'mappings_info', 'view_model');
var aboutToLooseMyJobInvokation = Knockback.Observer('this', 'is', 'un-intuative');
//goodbye global namespace
alert(window.model); // this
alert(window.mappings_info); // is
alert(window.view_model); // un-intuative
//goodbye job at reputable web firm
Id like to point out that the boys as ES5 camp fixed this problem, but strict mode is not implemented in all browsers yet (IE.. ahem ahem)

Add method with parameters to javascript object

I am currently in process of porting one of my java applet games to javascript+html5. I have never done object oriented javascript before and this prototype based OO stuff is confusing me a lot.
I tried to do a straightforward port from java but am having trouble doing two things:
1) How do I run a function inside a constructor?
2) How do I add a method that has a parameter?
Heres some example code:
function User()
{
setupStats();// I wanted to put some of the variable initializations into
// a separate function for code modularity reasons.
this.name='bob';
//However that doesn't seem to work
alert(this.gold); // gets Undefined
alert(this.name); // gets bob. Phew at least this works
//I also want to add a method with a parameter in it:
this.draw=function(ctx){drawUser(ctx);};
}
function setupStats()
{
this.gold=2;
this.exp=3;
this.blah='blah';
this.that='something else';
this.superultraomg='insert some computation';
}
function drawUser(ctx)
{
ctx.drawImage(blah,blah,blah);
alert(ctx); // Also gets undefined. Uh oh...
alert(this.name); //Undefined? WHAT IS THIS I DONT EVEN...
}
Please help guys!
Example
We are using prototype, to share the defaults in setupStats with all Users. We are using call to pass a context, being the User object, and a parameter;
function User()
{
setupStats();// I wanted to put some of the variable initializations into
// a separate function for code modularity reasons.
this.name='bob';
//However that doesn't seem to work
alert(this.gold); // gets Undefined
alert(this.name); // gets bob. Phew at least this works
//I also want to add a method with a parameter in it:
this.draw= function(ctx){ drawUser.call(this, ctx); };
}
function setupStats()
{
this.gold=2;
this.exp=3;
this.blah='blah';
this.that='something else';
this.superultraomg='insert some computation';
}
User.prototype = new setupStats();
new User().draw('pinky');
function drawUser(ctx)
{
//ctx.drawImage(blah,blah,blah);
alert(ctx); // Also gets undefined. Uh oh...
alert(this.name); //Undefined? WHAT IS THIS I DONT EVEN...
}
You aren't too far off. The trouble is mostly your use of the 'this' keyword.
You want something more like:
var user = {};
var user.setupStats = function ()
{
this.gold=2;
this.exp=3;
this.blah='blah';
this.that='something else';
this.superultraomg='insert some computation';
};
var user.init = function ()
{
this.name='bob';
//Setup the stats
this.setupStats();
//However that doesn't seem to work
alert(this.gold); // gets Undefined
alert(this.name); // gets bob. Phew at least this works
//I also want to add a method with a parameter in it:
this.draw=function(ctx){drawUser(ctx);};
};
You would continue this approach and execute calls against it by doing things like
user.init();
which would automatically chain your function references together.
I recommend reading JavaScript: The World's Most Misunderstood Programming Language by Douglas Crockford. He explains clearly how classes, private members, public members, inheritance, etc. are done in JavaScript.
You may want to consider encasing these methods in class scope, if there is still method ambiguity you can use dot notation to resolve the namespace ambiguity. this.name works because it is defined in the same function, however other functions do not know that they are intended to exist in the same scope, thus they return undefined.
ctx is not defined in drawUser() because the parameters are declared incorrectly. Javascrpit params should be delared as (NB they do not take the var keyword):
function methodName( aParam : aParamType, bParam : bParamType) {}
classes are declared using the class keyword [optional, omit square brackets]
[private public static] class ClassName [extends ParentClass] { /*methods here*/ }
hope this helps.

Javascript Prototype not Working

Hi I don't know whether this is my mistake in understanding Javascript prototype object ..
Well to be clear I'm new to the Javascript singleton concept and lack clear cut knowledge in that but going through some referral sites I made a sample code for my system but it's giving out some errors which I couldn't find why so I'm asking for your help. My code is:
referrelSystem = function(){
//Some code here
}();
Prototype function:
referrelSystem.prototype.postToFb = function(){
//Some Code here
};
I get an error saying prototype is undefined!
Excuse me i thought of this right now
EDIT
I have used like this:
referrelSystem = function(){
return{
login:getSignedIn,
initTwitter:initTw
}
};
Is this causing an issue?
A typical way to define a JavaScript class with prototypes would be:
function ReferrelSystem() {
// this is your constructor
// use this.foo = bar to assign properties
}
ReferrelSystem.prototype.postToFb = function () {
// this is a class method
};
You might have been confused with the self-executing function syntax (closures). That is used when you would like to have "private" members in your class. Anything you declare in this closure will only be visible within the closure itself:
var ReferrelSystem = (function () {
function doSomething() {
// this is a "private" function
// make sure you call it with doSomething.call(this)
// to be able to access class members
}
var cnt; // this is a "private" property
function RS() {
// this is your constructor
}
RS.prototype.postToFb = function () {
// this is a class method
};
return RS;
})();
I would recommend that you study common module patterns if you're looking into creating a library.
Update: Seeing your updated code, the return from referrelSystem won't work as expected, since return values are discarded when calling new referrelSystem().
Rather than returning an object, set those properties to this (the instance of referrelSystem that gets constructed):
var referrelSystem = function () {
// I assume you have other code here
this.login = getSignedIn;
this.initTwitter = initTw;
};
I don't think you intend to immediately execute the functions, change them to this:
var referrelSystem = function(){
//Some code here
};
(+var, -())
Same with the prototype function:
referrelSystem.prototype.postToFb = function(){
//Some Code here
};
(Here you don't need the var, because you're assigning to something that already exists.)
A function should return to work as
prototype
property.
Take a look at this example here

Categories