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 ...
Related
I'm trying to create an Object containing other Objects and functions, in a prototype, the relevant part is the UI prototype;
var fChat = function() {
this.debug = true;
};
fChat.prototype = {
constructor: fChat,
Log: function(str){
if(this.debug){
console.log(str);
}
},
UI: {
Login: {
Show: function(){
this.Log("UI.Login.Show()");
}
}
}
};
var fChatInstance = new fChat();
fChatInstance.UI.Login.Show();
When i call fChatInstance.UI.Login.Show() It give me an error:
Uncaught TypeError: this.Log is not a function
Is that because by using this is on another scope?
Usually i do var self = this;at the start of a prototype, but i don't know how I can do that by using an Object prototype.
Yes. The problem is the javascript dynamic binding of this, to fix it you can set "this" to the object by using bind function. Change the fchat function refactor it like this:
var fChat = function() {
this.debug = true;
this.UI.Login.Show = this.UI.Login.Show.bind(this);
this.Log = this.Log.bind(this);
};
I would like use the following syntax where the parameter is an ID of HTML element, very similar as to how you setup JWPlayer but I can't figure out how they did it. This is so I can make it as simple as possible for someone else to use.
myWidget("htmlTargetId");
I'm trying to avoid having to do:
myWidget = new MyWidget("htmlTargetId");
I know that I can create the first by doing:
var myWidget = function(target) {
// Do something here
}
myWidget("htmlTargetId");
I need to add methods and properties etc but I would like a "constructor" that will create elements in the "htmlTargetId". What would be the best way to do this?
I tried a few variations, this is the latest attempt:
var myWidget = (function () {
var _target = undefined;
// constructor
var widget = function (target) {
_target = target;
version = 12;
};
widget.prototype = {
constructor: widget,
doSomething: function () {
console.log("I will so something to", target);
}
};
return widget;
})();
// Try out the new code
myWidget("htmlTargetId");
console.log(myWidget.version);
myWidget.doSomething();
But this gives me "undefined" and "Uncaught TypeError: undefined is not a function" I assume this is because the return statement is returning a function rather than an object because I'm not using "new"?
// Trying to avoid having to do this
superWidget = new myWidget("htmlTargetId");
Many thanks!
If you want to have multiple Widget instances,
var myWidget = (function () {
// constructor
var Widget = function (target) {
this._target = target;
};
Widget.prototype = {
constructor: Widget,
version: 12,
doSomething: function () {
console.log("...", this._target);
}
};
return function init(target) {
return new Widget(target);
};
})();
var widget1 = myWidget("foo"),
widget2 = myWidget("bar");
console.log(widget1.version); // 12
widget1.doSomething(); // "..." "foo"
widget2.doSomething(); // "..." "bar"
However, if you only need one "instance", you don't need any constructor:
var myWidget = function (target) {
myWidget._target = target;
};
myWidget.version = 12;
myWidget.doSomething = function () {
console.log("...", myWidget._target);
}
myWidget("foo");
console.log(myWidget.version); // 12
myWidget.doSomething(); // "..." "foo"
I have this class definition:
$.note = function() {}
$.note.prototype = {
init: function(note) {
this.note = note;
this.ctrl = document.getElementById(note);
},
// I have these getter functions because I was getting errors using
// myObject.note or myObject.ctrl
getNote: function() {
return this.note;
},
getCtrl: function() {
return this.ctrl;
}
}
I created a new object with this class like this:
var note = new $.note('C');
Which I can access in my console like this:
But when I try and access note.getNote(), I get undefined as the response:
Am I going about accessing these properties incorrectly? I've tried using just note.note or note.ctrl, and I get the same thing...
Nothing's going to call that "init" function if you don't.
$.note = function(note) { this.init(note); }
Some frameworks provide an object system that uses constructor helper functions like that, but plain JavaScript doesn't.
Try this:
$.note = function(note) { this.note = note;}
or you should call init function:
var note = new $.note();
note.init('C');
Say I have this code:
function ParentClass()
{
var anArray = [ ];
this.addToArray = function(what)
{
anArray.push(what);
console.log(anArray);
};
}
FirstSubClass.prototype = new ParentClass();
FirstSubClass.prototype.constructor = FirstSubClass;
function FirstSubClass()
{
this.addToArray('FirstSubClass');
}
SecondSubClass.prototype = new ParentClass();
SecondSubClass.prototype.constructor = SecondSubClass;
function SecondSubClass()
{
this.addToArray('SecondSubClass');
}
When I run new FirstSubClass() I see a single value array in the console. And when I run new SecondSubClass(), again, I see a single value array.
However, why is it when I run them again (i.e. new FirstSubClass(); new SecondSubClass();) I then see the arrays added to rather than new ones being created?
The rationale here is that I'm creating new instances of a class, therefore why are they sharing the same private property?
How can I avoid this so when I do, for e.g., new FirstSubClass() I then see a single value array no matter how many times I create a new instance of the class?
Keep in mind that you've only called new ParentClass() once for each subclass. That means that the private array variable is part of the prototype object for those subclasses. There's only one prototype object, so there's only one array (per subclass).
Each call to new FirstSubClass() generates a new instance that shares the same prototype object. The call to addToArray() therefore adds an element to that same array that was created when the prototype object was created.
edit — if you want per-instance arrays, you'd have to do something like this:
function ParentClass() {
this.addToArray = function(value) { this.instanceArray.push(value); };
};
function FirstSubClass() {
this.instanceArray = [];
this.addToArray("First");
}
FirstSubClass.prototype = new ParentClass();
FirstSubClass.prototype.constructor = FirstSubClass;
First, sub-classing in JS is typically a bad idea, because people think that they're getting extension, where every instance has its own copy of properties and methods...
...really, they're getting public static access to the parent's stuff.
Even better, that public static stuff has no access to the encapsulated variables, so there's really no manipulation of private data, unless you're using private functions (with a public interface) to pass data to and collect return values from, the public static stuff.
var Parent = function () {
this.static_prop = 0;
this.static_method = function (num) { this.static_prop += 1; return num + this.static_prop; };
};
var Child = function (num) {
this.public_func = function () { num = this.static_method(num); };
};
Child.prototype = new Parent();
var child = new Child(13);
child.public_func();
Just calling this.static_method wouldn't help, because it would have 0 access to num, which means that you're wrapping things which you inherited to grant them access to use private data as inputs, which means that you're doing most of the writing you'd be doing anyway, regardless of inheritance, because your expectations of .prototype were backwards.
Might I suggest Dependency Injection, instead?
Component-based programs?
var Iterator = function () {
var count = 0,
min = 0,
max = 0,
reset = function () { count = min; },
next = function () { count = count >= max ? min : count; return count += 1; },
set_min = function (val) { min = val; },
set_max = function (val) { max = val; },
public_interface = { reset : reset, count : count, set_min : set_min, set_max : set_max };
return public_interface;
},
Thing = function (iter) {
var arr = [],
currentObj = null,
nextObj = function () {
currentObj = arr[iter.next()];
},
add = function (obj) {
arr.push(obj); iter.set_max(arr.length);
},
public_interface = { next : nextObj, add : add };
return public_interface;
};
var thing = Thing(Iterator());
thing.add({});
thing.next();
It's a convoluted example, but now every instance is going to be given exactly what it needs to do its job (because the constructor requires it -- or you can add the dependency later, through a public method, or as a public-property).
The interfaces for each module can now also get as simple and as clean as you'd like, as you don't have to wrap unexpected static-helpers to get private data...
Now you know what's private, you know what you're extending to the public, and you have clean ins and outs wherever you want to put them.
You are only constructing a new instance of ParentClass once per subclass and that is to apply it to your prototype. If you want each instance to have its own copy of the private array and its own copy of the function "addToArray" you will need to invoke the ParentClass constructor function within your other objects constructors:
function ParentClass(){
var anArray = [ ];
this.addToArray = function(what){
anArray.push(what);
console.log(anArray);
};
}
FirstSubClass.prototype = new ParentClass();
FirstSubClass.prototype.constructor = FirstSubClass;
function FirstSubClass(){
//call the parents constructor where "this" points to your FirstSubClass instance
ParentClass.call( this );
this.addToArray('FirstSubClass');
}
SecondSubClass.prototype = new ParentClass();
SecondSubClass.prototype.constructor = SecondSubClass;
function SecondSubClass(){
ParentClass.call( this );
this.addToArray('SecondSubClass');
}
try this:
http://jsfiddle.net/3z5AX/2/
function ParentClass()
{
var anArray = [ ];
this.addToArray = function(what)
{
anArray.push(what);
document.getElementById("i").value = anArray;
};
}
//FirstSubClass.prototype = new ParentClass();
FirstSubClass.prototype.constructor = FirstSubClass;
function FirstSubClass()
{
this.parent = new ParentClass()
this.parent.addToArray('FirstSubClass');
}
var q = new FirstSubClass();
var r = new FirstSubClass();
All Subclasses share the same parent class, thus the same private anArray
The solution is to use the Mixin pattern.
// I have the habbit of starting a mixin with $
var $AddToArray = function(obj) {
var array = [];
obj.addToArray = function(what) {
array.push(what);
console.log(array);
};
}
var FirstClass = function() {
$AddToArray(this);
}
var SecondClass = function() {
$AddToArray(this);
}
I have an existing class I need to convert so I can append functions like my_class.prototype.my_funcs.afucntion = function(){ alert(private_var);} after the main object definition. What's the best/easiest method for converting an existing class to use this method? Currently I have a JavaScript object constructed like this:
var my_class = function (){
var private_var = '';
var private_int = 0
var private_var2 = '';
[...]
var private_func1 = function(id) {
return document.getElementById(id);
};
var private_func2 = function(id) {
alert(id);
};
return{
public_func1: function(){
},
my_funcs: {
do_this: function{
},
do_that: function(){
}
}
}
}();
Unfortunately, currently, I need to dynamically add functions and methods to this object with PHP based on user selected settings, there could be no functions added or 50. This is making adding features very complicated because to add a my_class.my_funcs.afunction(); function, I have to add a PHP call inside the JS file so it can access the private variables, and it just makes everything so messy.
I want to be able to use the prototype method so I can clean out all of the PHP calls inside the main JS file.
Try declaring your "Class" like this:
var MyClass = function () {
// Private variables and functions
var privateVar = '',
privateNum = 0,
privateVar2 = '',
privateFn = function (arg) {
return arg + privateNum;
};
// Public variables and functions
this.publicVar = '';
this.publicNum = 0;
this.publicVar2 = '';
this.publicFn = function () {
return 'foo';
};
this.publicObject = {
'property': 'value',
'fn': function () {
return 'bar';
}
};
};
You can augment this object by adding properties to its prototype (but they won't be accessible unless you create an instance of this class)
MyClass.prototype.aFunction = function (arg1, arg2) {
return arg1 + arg2 + this.publicNum;
// Has access to public members of the current instance
};
Helpful?
Edit: Make sure you create an instance of MyClass or nothing will work properly.
// Correct
var instance = new MyClass();
instance.publicFn(); //-> 'foo'
// Incorrect
MyClass.publicFn(); //-> TypeError
Okay, so the way you're constructing a class is different than what I usually do, but I was able to get the below working:
var my_class = function() {
var fn = function() {
this.do_this = function() { alert("do this"); }
this.do_that = function() { alert("do that"); }
}
return {
public_func1: function() { alert("public func1"); },
fn: fn,
my_funcs: new fn()
}
}
var instance = new my_class();
instance.fn.prototype.do_something_else = function() {
alert("doing something else");
}
instance.my_funcs.do_something_else();
As to what's happening [Edited]:
I changed your my_funcs object to a private method 'fn'
I passed a reference to it to a similar name 'fn' in the return object instance so that you can prototype it.
I made my_funcs an instance of the private member fn so that it will be able to execute all of the fn methods
Hope it helps, - Kevin
Maybe I'm missing what it is you're trying to do, but can't you just assign the prototype to the instance once you create it? So, first create your prototype object:
proto = function(){
var proto_func = function() {
return 'new proto func';
};
return {proto_func: proto_func};
}();
Then use it:
instance = new my_class();
instance.prototype = proto;
alert(instance.prototype.proto_func());