JavaScript is a proper OOP language. It's not class-based like you're used to (it's prototype-based), but it is absolutely object-oriented -- very much so.
You can simulate classes and class-based inheritance in JavaScript (it's that powerful), which can be useful for people like you (and me) with backgrounds in that world. But classes are not the only way to modularize, which is really what you're talking about doing.
The solution presented in your question creates a function backed by an object (the prototype) which you can then use to create another object (the instance) that then does what you want. That fits the class-based mindset (which may be a good enough reason to do it!), and it's what I do for things I may need to create more than one of, but in some sense it may be overkill for this purpose, since you're not going to have multiple instances of your page objects at any given time. It also means that your click handlers won't work as defined in your example code -- when they're called in response to a click, the this
value will be wrong, it won't point to your ReservationPage
instance. (Functions in JavaScript are just functions, they're not methods; this
is defined by how the function is called, not how it's defined. Surprising to people like us, but it turns out that it's incredibly useful.) That can be worked around, of course, but it requires...working around it.
Since you're only going to need one instantiated object for the page, rather than all the hassle of constructor functions and prototypes such, why not just have one object for the page? And you can use a closure (function) to put scope around that object so it can have easily-accessed variables without having to worry about this
and without polluting the global namespace. Here's an example of doing that:
// Create a closure (function) to scope our variables, assign its
// return value to a variable for our page
var reservationPage = (function() {
var somePageData; // A private variable for our page
// Our load function; we'll export that below
function onLoad() {
// Hook some elements
$('submit-button').bind('click', submit_click);
$('clear-button').bind('click', clear_click);
// Set up some page data
somePageData = 42;
}
// A private function for handling the click of the submit button
function submit_click() {
alert('You just clicked the submit button; page data is ' + somePageData);
}
// A private function for handling the click of the clear button
function clear_click() {
alert('You just clicked the clear button; page data is ' + somePageData);
}
// Export any functions we may need outside the closure
return {onLoad: onLoad};
})();
$(document).ready(reservationPage.onLoad);
Note that all of the functions have access to the somePageData
variable, and to each other, but we didn't have to worry about this
. That's because the functions are declared within the scope in which the somePageData
is declared, and so they "close over" it (have access to it). That access is "live", it's not a copy or something.
Note that I've wrapped the entire thing in a function. I'm doing that because we want to avoid just dumping everything in the global namespace, because A) it's the Right Thing(tm), and B) you're combining all of your pages into one file you reuse. You could have four or five (or 20) page objects in your combined file, all definining a var with that same name, and they'd be kept distinct because they're declared inside separate functions. Similarly, the functions for the page (onLoad
and the like) are scoped to the function. Using a scoping function like this is called the "module pattern."
I also didn't bother to export any more than I had to make public (just the onLoad
function). There's no need for our handlers to be available outside the closure.
Another aspect of the above that can be useful: All of the functions we care about have names. Named functions help tools help you, by telling you where an exception occurred, etc. There's a big difference, from a tool's point of view, between an anonymous function assigned to a variable:
var fname = function() { /* ... */ };
...and a named function:
function fname() { /* ... */ };
This is just a side-note, though; you can do that with the code in your question, too:
ReservationPage.prototype = (function(){
function submit_click() {
alert(' you just clicked the submit button');
}
function clear_click() {
alert('button for clearning the form fields!!');
}
return {
constructor: ReservationPage,
submit_click: submit_click,
clear_click: clear_click
};
})();
Now, I wouldn't use the "one object" pattern for anything general purpose that I might want more than one of; that's what classes (or things like classes) are for. Hence the links above to simulating classes and solving the this
problem. :-) But for modularizing and organizing the script that's specific to the page, classes may be overkill.