Commonly accepted best practices around code organization in JavaScript

50,974

Solution 1

It would be a lot nicer if javascript had namespaces built in, but I find that organizing things like Dustin Diaz describes here helps me a lot.

var DED = (function() {

    var private_var;

    function private_method()
    {
        // do stuff here
    }

    return {
        method_1 : function()
            {
                // do stuff here
            },
        method_2 : function()
            {
                // do stuff here
            }
    };
})();

I put different "namespaces" and sometimes individual classes in separate files. Usually I start with one file and as a class or namespace gets big enough to warrant it, I separate it out into its own file. Using a tool to combine all you files for production is an excellent idea as well.

Solution 2

I try to avoid including any javascript with the HTML. All the code is encapsulated into classes and each class is in its own file. For development, I have separate <script> tags to include each js file, but they get merged into a single larger package for production to reduce the overhead of the HTTP requests.

Typically, I'll have a single 'main' js file for each application. So, if I was writing a "survey" application, i would have a js file called "survey.js". This would contain the entry point into the jQuery code. I create jQuery references during instantiation and then pass them into my objects as parameters. This means that the javascript classes are 'pure' and don't contain any references to CSS ids or classnames.

// file: survey.js
$(document).ready(function() {
  var jS = $('#surveycontainer');
  var jB = $('#dimscreencontainer');
  var d = new DimScreen({container: jB});
  var s = new Survey({container: jS, DimScreen: d});
  s.show();
});

I also find naming convention to be important for readability. For example: I prepend 'j' to all jQuery instances.

In the above example, there is a class called DimScreen. (Assume this dims the screen and pops up an alert box.) It needs a div element that it can enlarge to cover the screen, and then add an alert box, so I pass in a jQuery object. jQuery has a plug-in concept, but it seemed limiting (e.g. instances are not persistent and cannot be accessed) with no real upside. So the DimScreen class would be a standard javascript class that just happens to use jQuery.

// file: dimscreen.js
function DimScreen(opts) { 
   this.jB = opts.container;
   // ...
}; // need the semi-colon for minimizing!


DimScreen.prototype.draw = function(msg) {
  var me = this;
  me.jB.addClass('fullscreen').append('<div>'+msg+'</div>');
  //...
};

I've built some fairly complex appliations using this approach.

Solution 3

You can break up your scripts into separate files for development, then create a "release" version where you cram them all together and run YUI Compressor or something similar on it.

Solution 4

Inspired by earlier posts I made a copy of Rakefile and vendor directories distributed with WysiHat (a RTE mentioned by changelog) and made a few modifications to include code-checking with JSLint and minification with YUI Compressor.

The idea is to use Sprockets (from WysiHat) to merge multiple JavaScripts into one file, check syntax of the merged file with JSLint and minify it with YUI Compressor before distribution.

Prerequisites

  • Java Runtime
  • ruby and rake gem
  • You should know how to put a JAR into Classpath

Now do

  1. Download Rhino and put the JAR ("js.jar") to your classpath
  2. Download YUI Compressor and put the JAR (build/yuicompressor-xyz.jar) to your classpath
  3. Download WysiHat and copy "vendor" directory to the root of your JavaScript project
  4. Download JSLint for Rhino and put it inside the "vendor" directory

Now create a file named "Rakefile" in the root directory of the JavaScript project and add the following content to it:

require 'rake'

ROOT            = File.expand_path(File.dirname(__FILE__))
OUTPUT_MERGED   = "final.js"
OUTPUT_MINIFIED = "final.min.js"

task :default => :check

desc "Merges the JavaScript sources."
task :merge do
  require File.join(ROOT, "vendor", "sprockets")

  environment  = Sprockets::Environment.new(".")
  preprocessor = Sprockets::Preprocessor.new(environment)

  %w(main.js).each do |filename|
    pathname = environment.find(filename)
    preprocessor.require(pathname.source_file)
  end

  output = preprocessor.output_file
  File.open(File.join(ROOT, OUTPUT_MERGED), 'w') { |f| f.write(output) }
end

desc "Check the JavaScript source with JSLint."
task :check => [:merge] do
  jslint_path = File.join(ROOT, "vendor", "jslint.js")

  sh 'java', 'org.mozilla.javascript.tools.shell.Main',
    jslint_path, OUTPUT_MERGED
end

desc "Minifies the JavaScript source."
task :minify => [:merge] do
  sh 'java', 'com.yahoo.platform.yui.compressor.Bootstrap', '-v',
    OUTPUT_MERGED, '-o', OUTPUT_MINIFIED
end

If you done everything correctly, you should be able to use the following commands in your console:

  • rake merge -- to merge different JavaScript files into one
  • rake check -- to check the syntax of your code (this is the default task, so you can simply type rake)
  • rake minify -- to prepare minified version of your JS code

On source merging

Using Sprockets, the JavaScript pre-processor you can include (or require) other JavaScript files. Use the following syntax to include other scripts from the initial file (named "main.js", but you can change that in the Rakefile):

(function() {
//= require "subdir/jsfile.js"
//= require "anotherfile.js"

    // some code that depends on included files
    // note that all included files can be in the same private scope
})();

And then...

Take a look at Rakefile provided with WysiHat to set the automated unit testing up. Nice stuff :)

And now for the answer

This does not answer the original question very well. I know and I'm sorry about that, but I've posted it here because I hope it may be useful to someone else to organize their mess.

My approach to the problem is to do as much object-oriented modelling I can and separate implementations into different files. Then the handlers should be as short as possible. The example with List singleton is also nice one.

And namespaces... well they can be imitated by deeper object structure.

if (typeof org === 'undefined') {
    var org = {};
}

if (!org.hasOwnProperty('example')) {
    org.example = {};
}

org.example.AnotherObject = function () {
    // constructor body
};

I'm not big fan of imitations, but this can be helpful if you have many objects that you would like to move out of the global scope.

Solution 5

The code organization requires adoption of conventions and documentation standards:
1. Namespace code for a physical file;

Exc = {};


2. Group classes in these namespaces javascript;
3. Set Prototypes or related functions or classes for representing real-world objects;

Exc = {};
Exc.ui = {};
Exc.ui.maskedInput = function (mask) {
    this.mask = mask;
    ...
};
Exc.ui.domTips = function (dom, tips) {
    this.dom = gift;
    this.tips = tips;
    ...
};


4. Set conventions to improve the code. For example, group all of its internal functions or methods in its class attribute of an object type.

Exc.ui.domTips = function (dom, tips) {
    this.dom = gift;
    this.tips = tips;
    this.internal = {
        widthEstimates: function (tips) {
            ...
        }
        formatTips: function () {
            ...
        }
    };
    ...
};


5. Make documentation of namespaces, classes, methods and variables. Where necessary also discuss some of the code (some FIs and Fors, they usually implement important logic of the code).

/**
  * Namespace <i> Example </i> created to group other namespaces of the "Example".  
  */
Exc = {};
/**
  * Namespace <i> ui </i> created with the aim of grouping namespaces user interface.
  */
Exc.ui = {};

/**
  * Class <i> maskdInput </i> used to add an input HTML formatting capabilities and validation of data and information.
  * @ Param {String} mask - mask validation of input data.
  */
Exc.ui.maskedInput = function (mask) {
    this.mask = mask;
    ...
};

/**
  * Class <i> domTips </i> used to add an HTML element the ability to present tips and information about its function or rule input etc..
  * @ Param {String} id - id of the HTML element.
  * @ Param {String} tips - tips on the element that will appear when the mouse is over the element whose identifier is id <i> </i>.
  */
  Exc.ui.domTips = function (id, tips) {
    this.domID = id;
    this.tips = tips;
    ...
};


These are just some tips, but that has greatly helped in organizing the code. Remember you must have discipline to succeed!

Share:
50,974
user1709101
Author by

user1709101

Designer, developer, geek-dad

Updated on July 14, 2022

Comments

  • user1709101
    user1709101 almost 2 years

    As JavaScript frameworks like jQuery make client side web applications richer and more functional, I've started to notice one problem...

    How in the world do you keep this organized?

    • Put all your handlers in one spot and write functions for all the events?
    • Create function/classes to wrap all your functionality?
    • Write like crazy and just hope it works out for the best?
    • Give up and get a new career?

    I mention jQuery, but it's really any JavaScript code in general. I'm finding that as lines upon lines begin to pile up, it gets harder to manage the script files or find what you are looking for. Quite possibly the biggest propblems I've found is there are so many ways to do the same thing, it's hard to know which one is the current commonly accepted best practice.

    Are there any general recommendations on the best way to keep your .js files as nice and neat as the rest of your application? Or is this just a matter of IDE? Is there a better option out there?


    EDIT

    This question was intended to be more about code organization and not file organization. There has been some really good examples of merging files or splitting content around.

    My question is: what is the current commonly accepted best practice way to organize your actual code? What is your way, or even a recommended way to interact with page elements and create reuseable code that doesn't conflict with each other?

    Some people have listed namespaces which is a good idea. What are some other ways, more specifically dealing with elements on the page and keeping the code organized and neat?

  • DOK
    DOK over 15 years
    Is this what you were looking for? koders.com/javascript/…
  • matt lohkamp
    matt lohkamp over 15 years
    good commenting is KEY - I'm glad you said it so I didn't have to. I would add consistent naming conventions, some sort of easily comprehensible organization strategy for variables &amp; functions, and as you mentioned, judicious use of classes versus singletons.
  • Rhys
    Rhys over 15 years
    Yup, that's the one! :) Seems like the code behind the link is quite newer than the version I was inspired by, though. Thanks for your effort!
  • user1709101
    user1709101 over 15 years
    I'd agree with you about really long, chained commands, but one of the best parts of jQuery is that it keeps all Javascript out of HTML. You can setup event handlers for all your elements without "needing" to add IDs or on<whatever> events on your elements. As always, over use of any tool is bad...
  • Apex
    Apex almost 15 years
    getsprockets.org is the direct link
  • meouw
    meouw over 14 years
    Yup, I've used jmvc and it's pretty good - docs could be better though
  • Rinzler
    Rinzler over 13 years
    I usually refer to that as "the crockford way". +1 from me
  • MKroehnert
    MKroehnert over 13 years
    You can even go a bit further. See this link: wait-till-i.com/2007/08/22/…
  • graffic
    graffic over 13 years
    Nice for file organization. I do that with code. But in the end I compile my code in a... let's say dll. You need that with javascript too or you'll end up requesting 15 js files per page.
  • Adam Lassek
    Adam Lassek over 13 years
    This strikes me as a little cargo-culty. jQuery.fn is a pointer to jQuery.prototype, because $() actually returns a new instance of the jQuery constructor function. Adding a 'plugin' to jQuery means simply extending its prototype. But what you are doing is not that, and there are cleaner ways of accomplishing the same thing.
  • Chetan
    Chetan about 13 years
    I'm starting to use it, even for very little / basic things, and it really helps in keeping the code clean and flexible. It's worth using even for simple client-side JS manipulations.
  • Alex Heyd
    Alex Heyd about 13 years
    I believe he's simply creating static functions. I remember seeing it in jQuery's docs that this way of declaring static functions is acceptable
  • Marnen Laibow-Koser
    Marnen Laibow-Koser over 12 years
    I've worked on huge, well-organized projects in jQuery. I don't know why you think it gets in the way of organization.
  • Marnen Laibow-Koser
    Marnen Laibow-Koser over 12 years
    No. If you need comments, your code is generally not readable enough. Strive to write code that doesn't need comments.
  • Marnen Laibow-Koser
    Marnen Laibow-Koser over 12 years
    There's nothing wrong with requesting 15 JS files per page. Your browser will cache them for subsequent requests anyway.
  • jamiebarrow
    jamiebarrow over 12 years
    I find that using $ as a variable name prefix is a more common practice, but I could be wrong. So, $s = $('...') instead of jS = $('...'), just a matter of preference I guess. Interesting though, since Hungarian notation is thought to be a code smell. It's weird how different some of my JavaScript code conventions/preferences are to my C#/Java coding conventions.
  • Dan Abramov
    Dan Abramov over 12 years
    @jamie It isn't a code smell in this case, it's precisely one of the few cases where Hungarian is good. You might want to read this.
  • iwasrobbed
    iwasrobbed over 12 years
    @MarnenLaibow-Koser The only issue with requesting 15 JS files on a page is how many HTTP requests the browser can handle at a time. So bundling them into one file allows the browser to be requesting other necessary files at the same time.
  • Marnen Laibow-Koser
    Marnen Laibow-Koser over 12 years
    That's true, but after the first couple of hits, they'll be in the browser's cache, so they won't require HTTP connections.
  • jamiebarrow
    jamiebarrow over 12 years
    @DanAbramov thank you for the link. I really must read all of Joel's blogs, he explains things so well. Definitely deserves the fame/reputation he has. I will refer to Systems Hungarian as a code smell and Apps Hungarian as a practise from now on :)
  • jamiebarrow
    jamiebarrow over 12 years
    I guess in the C# world, it also could be a great article to promote the use of var, now that I think about it. Most the arguments against using var is where you won't be sure of the 'type' of what is returned, but I guess the argument should rather be against not know the 'class' of what is returned. If using Apps Hungarian, you shouldn't have that concern then... interesting.
  • Andreas
    Andreas over 11 years
    This i like, and use in my applications.
  • Marnen Laibow-Koser
    Marnen Laibow-Koser about 11 years
    The $ prefix is actually useless. Apps Hungarian is not appropriate here, because JS has a type system that will prevent you from doing the wrong things with your jQuery objects. Apps Hungarian is only desirable when the type system can't do your checking for you.
  • Marnen Laibow-Koser
    Marnen Laibow-Koser about 11 years
    I've done this in jQuery. JS has a built-in event model, so you don't really need much framework support.
  • Marnen Laibow-Koser
    Marnen Laibow-Koser about 11 years
    Also, if you need UML and state diagrams, that probably means that your architecture is not clear enough from the code. Downvoting.
  • Sean
    Sean almost 11 years
    @Marnen: I see your point, but it isn't useless as a guide to the programmer. The $ prefix reminds me what it is when reading my code later, and thus helps quicker understanding.
  • Marnen Laibow-Koser
    Marnen Laibow-Koser almost 11 years
    @Sean But it's still a bad idea. You shouldn't be doing work that the interpreter can already do. The interpreter has a robust type system, so rely on it. You don't write nPixels or strName, so don't write $element. Furthermore, it is useless as a guide: there's nothing stopping you from writing var $element = 3.
  • Cypher
    Cypher about 10 years
    @Marnen Well-written projects include comments to describe WHY, not necessarily WHAT. The code already describes the WHAT, but often times you need something to describe WHY. Upvoting.
  • Marnen Laibow-Koser
    Marnen Laibow-Koser almost 10 years
    @Cypher Well-written projects have clear enough code that you can usually tell the "why", not just the "what". I wouldn't trust a comment to tell me the "why", because I have no guarantee that it's in sync with the code. Let the code document itself.
  • Adrien Be
    Adrien Be over 9 years
    @MattBriggs otherwise called the module pattern and it is based on the IIFE pattern.
  • robsch
    robsch almost 8 years
    Don't you need to export classes somehow? How does an object get created from the outside of such a module? Or should there be a method createNewSomething() in the return object, so object creation happens solely within the module? Hm... I would expect that classes (constructors) are visisble from the outside.
  • oligofren
    oligofren almost 5 years
    @robsch His example doesn't take any parameters, but most would. See my example here for how this is usually done (TypeScript, but 99% same): repl.it/@fatso83/Module-Pattern-in-TypeScript