I am trying to write a javaScript application that is of sufficient size that modularity is probably a good idea. I am using the famous inherit function to enable objects to inherit from constructors that have parameters. The problem is I get an error that the parent being passed into the inherit function is undefined. This is because, well, the parent constructor hasn't been defined when inherit is called.
I have come up with a few ugly solutions to this problem:
Declare all child constructors after their parent (yuk).
Bind all prototype assignments to a custom event and use an ordered callback chain to ensure all the prototypes are assigned in order (perhaps there is potential there).
Move all prototype assignments from the area in the code that their constructor lives and consolidate (and exile) them to some 'assign prototypes' function (twice the yuk of item one, no?).
The bottom line is I don't want to have to base the order in which my constructors, methods, and objects are written in the code on the order in which the code is loaded/interpreted. I want my code to be easily extensible and with related code grouped together so it is easy to understand (i.e., shouldn't the prototype assignment be near the constructor declaration/definition?).
I am placing modules in separate files and using a PHP script to append them all together before they are sent to the browser. The PHP script only ensures the file that declares the namespace is included first, after that it concatenates the js files with glob.
I wrote a little script that demonstrates exactly the issue I am having. It is is live at this url. The screen is white because there is no html — I am using the console for analysis here.
/*global console, jQuery, $, myNS: true*/
/*jslint browser: true*/
/*
* This file is deliberately set as the first file in the php glob.
*/
var myNS = (function (myNS, $, window, undefined) {
"use strict";
/*
* @param {object} coords -- e.g. {x: 3, y: 26}
*/
myNS = function (coords) {
return new myNS.CartesianLoc(coords);
};
// static methods
myNS.inherit = function (parent) {
function F() {}
F.prototype = parent.prototype;
return new F();
};
myNS.getWinCenter = function () {
return {
x : $(window).width() / 2,
y : $(window).height() / 2
};
};
// prototype
myNS.prototype = {
log: function () {
if (window.console) {
console.log(this);
}
}
};
return myNS;
}(myNS, jQuery, window));
/*
* This is another file.
*/
(function (myNS, $, window, undefined) {
"use strict";
/*
* CartesianLoc constructor
*
* @param {object} coords -- given in a conceptual
* Cartesian space where the origin (0,0) is
* the middle of whatever screen
*/
function CartesianLoc(coords) {
myNS.Loc.call(this, coords);
}
CartesianLoc.prototype = myNS.inherit(myNS.Loc);
CartesianLoc.prototype.constructor = CartesianLoc;
CartesianLoc.prototype.getWinCoords = function () {
return {
x: myNS.getWinCenter().x + this.x,
y: myNS.getWinCenter().y + this.y
};
};
myNS.CartesianLoc = CartesianLoc;
}(myNS, jQuery, window));
/*
* This is another file.
*/
(function (myNS, $, window, undefined) {
"use strict";
// Location constructor
function Loc(coords) {
this.x = coords.x;
this.y = coords.y;
}
Loc.prototype = myNS.inherit(myNS);
Loc.prototype.constructor = Loc;
Loc.prototype.translate = function (coords) {
this.loc.x += coords.x;
this.loc.y += coords.y;
return this;
};
myNS.Loc = Loc;
}(myNS, jQuery, window));
/*
* Application js file
*
*/
(function (myNS, $, window, undefined) {
"use strict";
$(document).ready(function (event) {
if (console) {
console.log("%o", new myNS({x: 100, y: -45}));
}
});
}(myNS, jQuery, window));
Thanks for any help or ideas you can give me!
Chris