I have some code as part of a javascript weekend project that I'm working on. My problem is in the last line. As far as I can tell, I should be able to just call Floater.create() and not have to call Floater.prototype.create(). Why do I need the extra .prototype? Without it, an error is thrown: Floater() has no method "create"
function Floater(){}
Floater.prototype.create = function(){
//do stuff
}
$(document).ready(function(){
//do stuff
runStartup();
});
function runStartup(){
loginFloater = new Floater;
Floater.prototype.create();
// as far as I know, this should run as just Floater.create(),
// but that throws an error.
}
This part:
Floater.prototype.create = function(){
//do stuff
}
does not add a property to the Floater constructor, but to the [[Prototype]] object of instances created with new Floater(). So, Floater instances will have that method, but the constructor won't.
You may be looking to change your start-up function to:
function runStartup(){
loginFloater = new Floater();
loginFloater.create();
}
But, given the name of your method, you also may want to remove create altogether, and do initialization stuff directly from the constructor:
function Floater(){
// init stuff here
}
Related
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
So I have a function which looks like this:
function Students(){
// Function add:
// Returns: add page
this.add = function(){
// Get template
$.get('view/students/new.html', function(template){
// Templating
var html = Mustache.to_html(template);
// When document ready, write template to page
$('document').ready(function(){
$('#container').html(html);
});
});
};
};
When I try to call it's add function like so:
Students.add();
I get the error:
Uncaught TypeError: Object function Students(){...}has no method 'add'
What gives?
To use that implementation of Students, you should do this:
var students = new Students();
students.add();
However, that's probably not what you want. You probably meant to define Students like this:
var Students = {
add: function() {
$.get( /* ... */ );
}
};
Then you can call it like this:
Students.add();
Students is intended to be called as a constructor.
var s = new Students();
s.add()
Inside of Students, this will be a new object that inherits from Students's prorotype, and is returned automatically. So saying
this.add = function() ....
is adding the add function onto this object that's being returned. But the function will be created de novo each and every time you invoke this function. Why not add it to the prototype instead, so the function will exist only once, and not have to be needlessly re-created each and every time.
Students.prototype.add = function(){
You're not adding an "add" function to the "Students" function with that code; you're adding it to instances created by using "Students" as a constructor.
var student = new Students();
student.add(); // won't get the error
Well, Students doesn't have an add method.
I think you're assuming you know how this works. It isn't a reference to the function, unless you manually make it so. The value of this will depend entirely on how you call Students.
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.
I've sort if fell into this organization of javascript and was wondering if I'm missing the point somewhere here, or if there's a more elegant way of doing this.
Basically I'm wrapping everything in a function (object) and then setting up methods on that object, then instantiating an instance of the wrapper object and passing in any options and dependencies.
I have a hunch there's a way to automatically run .init() and a few other tweaks that could be made. Am I doing it right?
function AppModuleCore(){
var AppModuleCore = this; //keep internals sane
// Various global vars, objects
AppModuleCore.defaultOptions = {};
AppModuleCore.init = function(opts) {
// todo: that thing where you extend an options object a la juery
AppModuleCore.bindEvents();
};
AppModuleCore.bindEvents = function() {
// bind events here, send to functions within AppModuleCore.<FUNCTIONNAME>();
// Example:
$("a#clicker").unbind("click");
$("a#clicker").click(function(event){
AppModuleCore.handleClickerClick(event);
});
};
AppModuleCore.handleClickerClick = function(event){
alert("clicker was clicked");
};
}
// --------------------------------------------------------------------
// instantiate AppModuleCore object and initialize with opts,
// dependency injection
// --------------------------------------------------------------------
$(document).ready(function(){
AppModuleCore = new AppModuleCore;
var options = {};
AppModuleCore.init(options);
});
OK, some points
Having your code wrapped in a constructor only really makes sense if
You're going to instantiate more than one
You have "public" methods on the object that you are going to call
Your code doesn't exhibit these characteristics. I say this because your jQuery selectors a#clicker are hard coded so I'm assuming that you wouldn't want to bind the same events to them more than once?
You'd be better off using a function (perhaps your init) or an object literal to limit your scope..
function init( options ) {
var defaultsOptions = {};
var privateVar = 'only in this scope';
//extend your default options with options here
//using jquery
options = $.extend( defaultOptions, options );
// this function is completely private to this scope
function privatefunction() {
//do stuff
}
function handleClickerClick( event ){
alert("clicker was clicked");
}
// you don't need to wrap your handler in an anonymous function unless
// you're doing some work to the event before forwarding:- just give a
// reference to your handler
// the handler has access to other members of this scope, we're in a closure
$(options.selector).click( handleClickerClick );
//etc
}
init( {selector: 'a#clicker'} );
On a stylistic note: when you alias this with the same name as the constructor and then add methods to the alias, it looks at first glance like you are adding static methods to the constructor. This may be confusing to someone who looks at your code later and doesn't notice the alias.
function C() {
// a static method i.e a property of the constructor, C not objects created with it
// it is a bit wierd that it is defined in the constructor but not unheard of
C.staticMethod = function(){};
//quite plainly a method of objects of this type, easy to understand
this.method = function(){};
}
I have such JS class that have to be tested:
SomeClass = function {
// some stuff that uses initRequest
this.initRequest = function() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else if (window.ActiveXObject) {
return new ActiveXObject("Microsoft.XMLHTTP");
}
}
}
I want to override method initRequest for testing purposes. I tried to do something like that
var request = new MockXmlHttpRequest();
var instance = new SomeClass();
instance.initRequest = function() {
return request;
};
// some calls of the SomeClass methods that use initRequest
// some test code with assertions for the request
Still calling of the initRequest method calls actually the original code, but not the function that I tried to pass to instance.initRequest.
Any ideas what's wrong?
Check out YUI's extend functionality. It makes all this really clean, PLUS, you can still access the method of the super class if you really want to. Their augment object might also be of interest to you.
Here is the link http://developer.yahoo.com/yui/examples/yahoo/yahoo_extend.html
In your first example, initRequest returns a new object after each call; in the second example, all calls to initRequest return a reference to the one and only (mock) object.
The request object is not changed in the code example you provide. instance.initRequest simple returns a reference to request. Why do expect it to have changed?
I'm having a little trouble understanding your question because of your English, but I'll try to answer anyway (and if I answer the wrong question just let me know).
I think what you want to do is:
SomeClass = function {
// some stuff that uses initRequest
this.initRequest = function() {
return request;
}
}
In other words, you want to overwrite the original SomeClass object with a new function. Unless you do that new SomeClass objects won't use your test method, they'll just use the original method.
However, if you only want to override that method for a specific object, not for the whole class, what you have there should work. If that's the case, could you please clarify what exactly isn't working about it?
SomeClass.prototype.initRequest = function()
{
return request;
}
First of all, this code sample is not the best way of OOP in JavaScript - use prototype instead. Another remark, your sample throws error in my browser (invalid declarartion of SomeClass function, maybe it's only typing mistake). And at last, instance.initRequest actually returns (I've run it at Google Chrome) object you have expected - check please my sample.