user656925
user656925

Reputation:

JavaScript organization | Module pattern w/ modules

I'm organizing my code into 20-60 line modules, usually in the module pattern. I want a well formed object oriented JavaScript library.

Is this the best way to do this? The code has been tested and works.

I like it because a programmer can pull modules from the library and use them as needed, they are self contained.

Here is Tool, Message, Effect and Text, all contained in NS.

Question?

Is this a good way ( best practice ) to organize my library?

Note

So far, there is 0 consensus in the comments and answers...very frustrating.

Outer Module Pattern

var NS = ( function ( window, undefined ) 
{ 
/* All Modules below here */ 
} )( window );

Tools

/**
 *Tools
 *    getTimeLapse - benchmark for adding
 */

var Tool = ( function () 
{
    var Tool = function ( ) 
    {
    };
    Tool.prototype.getTimeLapse = function( numberOfAdds ) 
    {
        var end_time;
        var start_time = new Date().getTime();
        var index = 0;           
        while ( index <= numberOfAdds )
        {
            index++;
        }
        end_time = new Date().getTime();
        return ( end_time - start_time );
    };
    return Tool;
} () );

Message

/**
 *Message
 *    element - holds the element to send the message to via .innerHTML
 *    type - determines the message to send
 */

var Message = ( function () 
{
    var messages = 
    {
        name:         'Please enter a valid name',
        email:        'Please enter a valid email',
        email_s:      'Please enter a valid email.',
        pass:         'Please enter passoword, 6-40 characters',
        url:          'Please enter a valid url',
        title:        'Please enter a valid title',
        tweet:        'Please enter a valid tweet',
        empty:        'Please complete all fields',
        same:         'Please make emails equal',
        taken:        'Sorry, that email is taken',
        validate:     'Please contact <a class="d" href="mailto:[email protected]">support</a> to reset your password',
    };
    var Message = function (element) 
    {
        this.element = element;
    };
    Message.prototype.display = function( type ) 
    {
        this.element.innerHTML = messages[ type ];
    };
    return Message;
} () );

Effects

/**
 *Effects
 *    element - holds the element to fade
 *    direction - determines which way to fade the element
 *    max_time - length of the fade
 */

var Effects = ( function () 
{
    var Effects = function ( element )
    {
        this.element = element;
    };
    Effects.prototype.fade = function( direction, max_time ) 
    {
        var element = this.element;
        element.elapsed = 0;
        clearTimeout( element.timeout_id );
        function next()
        {
            element.elapsed += 10;
            if ( direction === 'up' )
            {
                element.style.opacity = element.elapsed / max_time;
            }
            else if ( direction === 'down' )
            {
                element.style.opacity = ( max_time - element.elapsed ) / max_time;
            }
            if ( element.elapsed <= max_time ) 
            {
                element.timeout_id = setTimeout( next, 10 );
            }
        }
        next();
    };
    return Effects;
} () );

Text

/**
 *Text
 *    form_elment - holds text to check
 */

var Text = ( function () 
{
    var Text = function ( form_element )
    {
        this.text_array = form_element.elements;
    };
    Text.prototype.patterns = 
    {
        prefix_url:     /^(http:)|(https:)\/\//,
        aml:            /<(.+)_([a-z]){1}>$/,
        url:            /^.{1,2048}$/,
        tweet:          /^.{1,40}$/, 
        title:          /^.{1,32}$/,
        name:           /^.{1,64}$/, 
        email:          /^.{1,64}@.{1,255}$/,
        pass:           /^.{6,20}$/
    };
    Text.prototype.checkPattern = function( type ) 
    {
        return this.patterns[ type ].exec( this.text_array[type].value );
    };
    Text.prototype.checkUrl = function( type ) 
    {
        return this.patterns[ type ].exec( this.text_array.url.value );
    };
    Text.prototype.checkSameEmail = function() 
    {
        return ( ( this.text_array.email.value ) === ( this.text_array.email1.value ) );
    };
    Text.prototype.checkEmpty = function() 
    {
        for ( var index = 0; index < this.text_array.length; ++index ) 
        {
            if ( this.text_array[ index ].value === '') 
            { 
                return 0; 
            }
        }
        return 1;
    };
    return Text;
} () );

Upvotes: 5

Views: 858

Answers (3)

GillesC
GillesC

Reputation: 10874

The one thing I would suggest to change to make your code cleaner and reduce its footprint is to just set the prototype property at once, so that instead of doing

Object.prototype.method1 = function(){};
Object.prototype.method2 = function(){};

You do

Object.prototype = {
    method1: function(){},
    method2: function(){}
};

If you need to conserve the constructor reference, which is recommened, you should re-assign the constructor afterward. See this answer for more details.

Upvotes: 2

Tracker1
Tracker1

Reputation: 19344

A few suggestions... First would be to create a namespace object as scope for your libraries... jQuery uses "jQuery" and "$", underscore uses "_". I tend to use "CompanyName.SiteName"

if (typeof CompanyName == "undefined") var CompanyName = {};
CompanyName.SiteName = CompanyName.SiteName || {};

The first line explicitly checks against undefined, as you'll get errors in many browsers otherwise for root variables using the method on the SiteName property.

From there, I would make a couple adjustments... When you are calling an anonymous function inline, it's best to wrap the whole of the call inside the parens.

CompanyName.SiteName.ModuleName = (function(w){
    ...
    return moduleImplementation;
}(window || this)); //CompanyName.SiteName.ModuleName

This tends to avoid the confusion by having the parens wrap the whole, and by having a comment at the end of the module declaration.

Per the comment above, you may want to make the prototype declaration as a more singular statement. I would advise against this, as longer modules can make readability an issue.

myModule.prototype = {
    "method1": function(){
    }
    ...
    "methodN": function(){
       //by the time you get here, you may not see the top, and where you are nested in
    }
};

//with the dot-notation, or hash notation
myModule.prototype.methodN = ...
myModule.prototype["methodN"] = ...
//you can see where you are binding to at that function

You may also want to look into RequireJS and AMD

There's also the concept of dealing with simpler objects, and using functional binders. Treating your library as a set of functions (similar to C exports) that are passed and work with simpler objects/types. It really depends on your needs/usage and the specifics of your needs and use.

You may also want to look at javascript libraries like KnockoutJS, Underscore and Backbone for some examples.

Upvotes: 1

Raynos
Raynos

Reputation: 169541

I personally prefer using a modular code organization library like ncore

This encourages you to write your code as a set of modules (one module per file) and then hook them together using dependency injection and bootstrapping.

The code is slightly portable because modules are just objects on their own right, however if one doesn't use ncore advantages are lost.

The leaderboard app shows a detailed example of OO code organization

Upvotes: 1

Related Questions