views:

62

answers:

1

Hi there,

i am trying to organize my javascript. What i would like to do is have a class for each PAGE rather then just entering javascript straight into a js - is this good practice?

Let me explain .. thinking a long of the lines of, i would really appreciated any input, good or bad and ideas on my variable naming... of remember that this a kind of class so i have to create the class but then i must call it...(see below) ... i am using the prototype + constructor pattern.

// THIS FILE WILL BE SAVED as RESERVATIONPAGE
function ReservationPage(){
    // Setup click events in this constructor
    $('submit-button').bind('click',this.submit_click);

    $('clear-button').bind('click',this.clear_click);

    // setup other variables   
}

ReservationPage.prototype = {
      constructor: ReservationPage,
      submit_click : function () {
            alert(' you just clicked the submit button');
      },
      clear_click : function () {
            alert('button for clearning the form fields!!');
      }
};

Then each page will need a standard js file i.e. not a class / object to instantiate their Page js like above..., so a kind of entry point - so hence

// THIS FILE WILL BE SAVED as ReservationLoad - notice the word Load :-) -- don't know if this is good naning convetion, anybody suggest anything different??

var reservationPage = new ReservationPage(); // CREATES NEW INSTANCE OF THE CLASS/OBJECT ..

I don't any other way of doing it, i would really appreciate any input. I will be using jquery extensively but this shouldn't matter..... I need a standard JS file - no objects to instantiate my objects hence I called in load.... this in turn instantiates the object and calls the constructor..

The constructor sets up the click events and then calls the methods

Is this good,

Any idea where i am going wrong..

I really appreciate some input, i am little lost...... and want to do it right... I am coming from a c# .net background..... and i know js isn't a proper OOP language but i am trying to treat it like this for better structuring...

This way, all my js files are separate from my HTML and my CSS:.. there is no events entered into the html... they are all entered via code

Look forward to any info

+2  A: 

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.

T.J. Crowder
Wow! This was so cool, it seems to be working... I have 1 question, just i would like to know what we are returning.. when we say return {onLoad: onLoad}; so the first onLoad is what is available in the js file which mapps to the onLoad which is the function insize the anonymous function?
mark smith
The `return {onLoad: onLoad};` returns the object that will be assigned to the symbol `reservationPage` (because we're assigning the result of our anonymous function call to that symbol). It creates an object using "object literal" notation and sets a property on that object called `onLoad` (the one one the left) to be the value of whatever the `onLoad` symbol on the right is. In our case, the `onLoad` symbol (on the right) is a reference to the `onLoad` function we've defined within the anonymous function. That's how we can call that function from outside the scoping function.
T.J. Crowder
*(Continuing)* This is one of the aspects of the module pattern: Everything within the scoping function is private, but we can return an object with properties pointing to those private things, and thereby "export" them. Glad it's working for you, best of luck.
T.J. Crowder
Thank you for a great explanation.
mark smith