Skip to content

Instantly share code, notes, and snippets.

@patocallaghan
Created January 29, 2014 16:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save patocallaghan/8691869 to your computer and use it in GitHub Desktop.
Save patocallaghan/8691869 to your computer and use it in GitHub Desktop.
Intercom JS

JavaScript Modules

##Why Modules? Writing your JavaScript in a modular fashion provides many benefits over writing in the traditional spaghetti style of sprawling global functions. Benefits include:

Less naming collisions

Hanging all your JavaScript code off of the window object`, pollutes the global scope and increases the chance of naming collisions within your own application and also with third-party libraries. It makes your code more error-prone and susceptible to interference from external sources. Writing your code with a module pattern hides your code from the global scope and only exposes what you want to.

Single Responsibility Principle

Modules make it easy to obey the Single Responsibility Principle. Your modules should do one thing and one thing well. Larger components should be built up from a combination of small tightly-focused modules.

Easier to test

Having small modules with a singular purpose make them much easier to unit test than large sprawling functions with complex intertwined responsibilities. A benefit of having your module unit tested is that it makes it trivial to refactor and improve your code over time without fear of breaking existing functionality.

Maintainability / easier to read

Small modules are much easier maintain as by their nature they are responsible for a specific area of functionality. This makes it easier to track down bugs and also to add new features over time.

Reusability

Abstracting your code into tightly-focused modules increase the chances of reusing these modules throughout the app. This results in code which is more tested and less prone to bugs. It also allows for more rapid development as you are essentially building on top of a library of tried-and-tested modules.

Module Format

Below is an example module format. It's based off of the Module Pattern.

;(function(IC, $, _){
    
    //Enables stricter syntax JavaScript parsing
    'use strict'
    
    //Set up a namespace for your module
    IC.namespace('Util.YourModule');
    
    //Implement your module
    IC.Util.YourModule = {
        ...
    };
    
    
})(window.IC || {}, jQuery, _);

While ideally it would be nice to take advantage of module patterns such as AMD or CommonJS, allowing you to specify your dependencies, it is too premature to do so. This could be a good next step once our codebase is has moved to a more modular structure.

Additional information: Why the leading semicolon? What is JavaScript strict mode?

Namespaces

By hanging lots of functions on the window object, it pollutes the global scope. Defining namespaces for your modules are a good way to prevent this. Our namespace strategy for the Intercom web app consists of only creating a single global variable IC, an acronym for Intercom. This was selected as it doesn't collide with the global namespace of the Intercom widget, Intercom.

Creating a namespace for your module is as simple as calling:

IC.namespace('App.View.ModuleName');

We are using the namespace function inspired by the YUI mixin which makes it trivial to create namespaces and avoid overwriting already existing namespaces. How it works is, if a particular path in the namespace already exists its properties are copied in, otherwise an empty object literal is created.

The window.IC namespace is created and bootstrapped in assets/javascripts/bootstrap.js with the namespace.

###IC.App Functionality specific to the app. Views and business logic.

###IC.Util Library of reusable non-view utility functions. e.g. Backbone Extend and Event objects, dates, string manipulation etc.

##Directory Structure While not an exact one-to-one mapping, module namespaces should try to mirror the structure of the app/assets/javascripts directory. Examples

  • IC.Util.Events => app/assets/javascripts/util/events.js file.
  • IC.App.View.Base => app/assets/javascripts/views/base.js file.
  • IC.App.View.Selector => app/assets/javascripts/views/selector.js file.

##Components TODO Talk about coding guidelines. For example:

  • only use js_ prefixed classes. No CSS styling classes, e.g. js_component.

  • State classes should be prefixed with js_is_ e.g. js_is_active.

  • Don't add variables to the prototype of the component unless it is a constant value that is to be shared amongst all instances of the component. If you want instance variables, create them within the intialize() function.

  • Trigger events off of the component's this context e.g. this.trigger('selected.all')

  • In the initialize() function, only call _.extend(this, options) after initializing all the component's default values. This guarantees that the passed-in options always take precedence over the default ones.

  • Event name strings are namespaced and dot-delimited, e.g. eventname.eventgroup. The right-most token of the event is the most generalised, referring to an event group. The left-most token referring to the specific event being triggered. Take the following examples:

    • selected.item
    • deselected.item
    • clicked.item
  • js_ HTML classes should not be used by CSS for styling. Instead provide the ability to customise the CSS state classes using the configuration options. For example:

//Component implementation
IC.App.View.Button = IC.App.View.Base.extend({
    initialize: function(options) {
        //Empty custom class by default
        this.button_css_active_class = '';
        _.extend(this, options);
    },
    activate: function(){
        //Add the JS specific class as well as the custom class for styling purposes
        this.$el
            .addClass('js_is_active ' + this.button_css_active_class);
    }
});

//Initialising the component with the custom class
var component = IC.App.View.Button.extend({
    button_css_active_class: 'is-selected'
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment