You are previewing Learning JavaScript Design Patterns.

Learning JavaScript Design Patterns

Cover of Learning JavaScript Design Patterns by Addy Osmani Published by O'Reilly Media, Inc.
  1. Learning JavaScript Design Patterns
  2. SPECIAL OFFER: Upgrade this ebook with O’Reilly
  3. Preface
    1. Target Audience
    2. Credits
    3. Reading
    4. Conventions Used in This Book
    5. Using Code Examples
    6. Safari® Books Online
    7. How to Contact Us
    8. Acknowledgments
  4. 1. Introduction
  5. 2. What Is a Pattern?
    1. We Already Use Patterns Every Day
  6. 3. “Pattern”-ity Testing, Proto-Patterns, and the Rule of Three
  7. 4. The Structure of a Design Pattern
  8. 5. Writing Design Patterns
  9. 6. Anti-Patterns
  10. 7. Categories of Design Patterns
    1. Creational Design Patterns
    2. Structural Design Patterns
    3. Behavioral Design Patterns
  11. 8. Design Pattern Categorization
    1. A Brief Note on Classes
  12. 9. JavaScript Design Patterns
    1. The Constructor Pattern
      1. Object Creation
      2. Basic Constructors
      3. Constructors with Prototypes
    2. The Module Pattern
      1. Object Literals
      2. The Module Pattern
      3. Module Pattern Variations
    3. The Revealing Module Pattern
      1. Advantages
      2. Disadvantages
    4. The Singleton Pattern
    5. The Observer Pattern
      1. Differences Between the Observer and Publish/Subscribe Pattern
      2. Advantages
      3. Disadvantages
      4. Publish/Subscribe Implementations
    6. The Mediator Pattern
      1. Basic Implementation
      2. Advanced Implementation
      3. Example
      4. Advantages and Disadvantages
      5. Mediator Versus Observer
      6. Mediator Versus Facade
    7. The Prototype Pattern
    8. The Command Pattern
    9. The Facade Pattern
      1. Notes on Abstraction
    10. The Factory Pattern
      1. When to Use the Factory Pattern
      2. When Not to Use the Factory Pattern
      3. Abstract Factories
    11. The Mixin Pattern
    12. Subclassing
    13. Mixins
      1. Advantages and Disadvantages
    14. The Decorator Pattern
    15. Pseudoclassical Decorators
      1. Interfaces
      2. Abstract Decorators
    16. Decorators with jQuery
    17. Advantages and Disadvantages
    18. Flyweight
      1. Using Flyweights
      2. Flyweights and Sharing Data
      3. Implementing Classical Flyweights
      4. Converting Code to Use the Flyweight Pattern
      5. A Basic Factory
      6. Managing the Extrinsic States
      7. The Flyweight Pattern and the DOM
  13. 10. JavaScript MV* Patterns
    1. MVC
      1. Smalltalk-80 MVC
    2. MVC for JavaScript Developers
      1. Models
      2. Views
      3. Controllers
      4. Controllers in Another Library (Spine.js) Versus Backbone.js
    3. What Does MVC Give Us?
    4. Smalltalk-80 MVC in JavaScript
      1. Delving Deeper
      2. Summary
    5. MVP
      1. Models, Views, and Presenters
      2. MVP or MVC?
      3. MVC, MVP, and Backbone.js
    6. MVVM
      1. History
      2. Model
      3. View
      4. ViewModel
      5. Recap: The View and the ViewModel
      6. Recap: The ViewModel and the Model
    7. Pros and Cons
      1. Advantages
      2. Disadvantages
    8. MVVM with Looser Data Bindings
    9. MVC Versus MVP Versus MVVM
    10. Backbone.js Versus KnockoutJS
  14. 11. Modern Modular JavaScript Design Patterns
    1. A Note on Script Loaders
    2. AMD
      1. Getting Started with Modules
      2. AMD Modules with Dojo
      3. AMD Module Design Patterns (Dojo)
      4. AMD Modules with jQuery
      5. AMD Conclusions
    3. CommonJS
      1. Getting Started
      2. Consuming Multiple Dependencies
      3. Loaders and Frameworks that Support CommonJS
      4. Is CommonJS Suitable for the Browser?
      5. Related Reading
    4. AMD and CommonJS: Competing, but Equally Valid Standards
      1. UMD: AMD and CommonJS-Compatible Modules for Plug-ins
    5. ES Harmony
      1. Modules with Imports and Exports
      2. Modules Loaded from Remote Sources
      3. Module Loader API
      4. CommonJS-like Modules for the Server
      5. Classes with Constructors, Getters, and Setters
      6. ES Harmony Conclusions
      7. Related Reading
    6. Conclusions
  15. 12. Design Patterns in jQuery
    1. The Composite Pattern
    2. The Adapter Pattern
    3. The Facade Pattern
    4. The Observer Pattern
    5. The Iterator Pattern
    6. Lazy Initialization
    7. The Proxy Pattern
    8. The Builder Pattern
  16. 13. jQuery Plug-in Design Patterns
    1. Patterns
    2. A Lightweight Start Pattern
      1. Further Reading
    3. Complete Widget Factory Pattern
      1. Further Reading
    4. Nested Namespacing Plug-in Pattern
      1. Further Reading
    5. Custom Events Plug-in Pattern (with the Widget Factory)
      1. Further Reading
    6. Prototypal Inheritance with the DOM-to-Object Bridge Pattern
      1. Further Reading
    7. jQuery UI Widget Factory Bridge Pattern
      1. Further Reading
    8. jQuery Mobile Widgets with the Widget Factory
    9. RequireJS and the jQuery UI Widget Factory
      1. Usage
      2. Further Reading
    10. Globally and Per-Call Overridable Options (Best Options Pattern)
      1. Further Reading
    11. A Highly Configurable and Mutable Plug-in Pattern
      1. Further Reading
    12. What Makes a Good Plug-in Beyond Patterns?
      1. Quality
      2. Code Style
      3. Compatibility
      4. Reliability
      5. Performance
      6. Documentation
      7. Likelihood of maintenance
    13. Conclusions
    14. Namespacing Patterns
    15. Namespacing Fundamentals
      1. Single Global Variables
      2. Prefix Namespacing
      3. Object Literal Notation
      4. Nested Namespacing
      5. Immediately Invoked Function Expressions (IIFE)s
      6. Namespace Injection
    16. Advanced Namespacing Patterns
      1. Automating Nested Namespacing
      2. Dependency Declaration Pattern
      3. Deep Object Extension
      4. Recommendation
  17. 14. Conclusions
  18. A. References
  19. Index
  20. About the Author
  21. Colophon
  22. SPECIAL OFFER: Upgrade this ebook with O’Reilly
  23. Copyright
O'Reilly logo

Advanced Namespacing Patterns

We’ll now explore some advanced patterns and utilities that I have found invaluable when working on larger applications, some of which have required a rethink of traditional approaches to application namespacing. I’ll note that I am not advocating any of the following as the way to namespace, but rather ways that I have found work in practice.

Automating Nested Namespacing

As we’ve reviewed, nested namespaces can provide an organized hierarchy of structure for a unit of code. An example of such a namespace could be the following: application.utilities.drawing.canvas.2d. This can also be expanded using the object literal pattern to be:

var application = {
      utilities:{
          drawing:{
              canvas:{
                  2d:{
                          //...
                  }
              }
          }
    }        
};

One of the obvious challenges with this pattern is that each additional layer we wish to create requires yet another object to be defined as a child of some parent in our top-level namespace. This can become particularly laborious when multiple depths are required as our application increases in complexity.

How can this problem be better solved? In JavaScript Patterns, Stoyan Stefanov presents a very clever approach for automatically defining nested namespaces under an existing global variable. He suggests a convenience method that takes a single string argument for a nest, parses this, and automatically populates our base namespace with the objects required.

The method he suggests using is the following, which I’ve updated it to be a generic function for easier reuse with multiple namespaces:

// top-level namespace being assigned an object literal
var myApp = myApp || {};

// a convenience function for parsing string namespaces and 
// automatically generating nested namespaces
function extend( ns, ns_string ) {
    var parts = ns_string.split("."),
        parent = ns,
        pl;

    pl = parts.length;

    for ( var i = 0; i < pl; i++ ) {
        // create a property if it doesn't exist
        if ( typeof parent[parts[i]] === "undefined" ) {
            parent[parts[i]] = {};
        }

        parent = parent[parts[i]];
    }

    return parent;
}

// Usage:
// extend myApp with a deeply nested namespace
var mod = extend(myApp, "myApp.modules.module2");

// the correct object with nested depths is output
console.log(mod);

// minor test to check the instance of mod can also
// be used outside of the myApp namesapce as a clone
// that includes the extensions 

// Outputs: true
console.log(mod == myApp.modules.module2); 

// further demonstration of easier nested namespace
// assignment using extend
extend(myApp, "moduleA.moduleB.moduleC.moduleD");
extend(myApp, "longer.version.looks.like.this");
console.log(myApp);

Figure 13-1 shows the Chrome Developer Tools output:

Chrome Developer Tools output

Figure 13-1. Chrome Developer Tools output

Where one would previously have had to explicitly declare the various nests for their namespace as objects, this can now be easily achieved using a single, cleaner line of code.

Dependency Declaration Pattern

We’re now going to explore a minor augmentation to the Nested Namespacing pattern which we’ll refer to as the Dependency Declaration pattern. We all know that local references to objects can decrease overall lookup times, but let’s apply this to namespacing to see how it might look in practice:

// common approach to accessing nested namespaces
myApp.utilities.math.fibonacci( 25 );
myApp.utilities.math.sin( 56 );
myApp.utilities.drawing.plot( 98,50,60 );

// with local/cached references
var utils = myApp.utilities,
maths = utils.math,
drawing = utils.drawing;

// easier to access the namespace
maths.fibonacci( 25 );
maths.sin( 56 );
drawing.plot( 98, 50,60 );

// note that the above is particularly performant when 
// compared to hundreds or thousands of calls to nested 
// namespaces vs. a local reference to the namespace

Working with a local variable here is almost always faster than working with a top-level global (e.g., myApp). It’s also both more convenient and more performant than accessing nested properties/subnamespaces on every subsequent line and can improve readability in more complex applications.

Stoyan recommends declaring localized namespaces required by a function or module at the top of our function scope (using the single-variable pattern) and calls this a Dependency Declaration pattern. One of the benefits this offers is a decrease in locating dependencies and resolving them, should we have an extendable architecture that dynamically loads modules into our namespace when required.

In my opinion, this pattern works best when working at a modular level, localizing a namespace to be used by a group of methods. Localizing namespaces on a per-function level, especially where there is significant overlap between namespace dependencies, would be something I would recommend avoiding where possible. Instead, define it further up and just have them all access the same reference.

Deep Object Extension

An alternative approach to automatic namespacing is deep object extension. Namespaces defined using object literal notation may be easily extended (or merged) with other objects (or namespaces) such that the properties and functions of both namespaces can be accessible under the same namespace post-merge.

This is something that’s been made fairly easy to accomplish with modern JavaScript frameworks (e.g., see jQuery’s $.extend); however, if looking to extend objects (namespaces) using vanilla JS, the following routine may be of assistance.

// extend.js
// Written by Andrew Dupont, optimized by Addy Osmani

function extend( destination, source ) {

    var toString = Object.prototype.toString,
        objTest = toString.call({});

    for ( var property in source ) {
        if ( source[property] && objTest === toString.call(source[property]) ) {
            destination[property] = destination[property] || {};
            extend(destination[property], source[property]);
        } else {
            destination[property] = source[property];
        }
    }
    return destination;

};

console.group( "objExtend namespacing tests" );

// define a top-level namespace for usage
var myNS = myNS || {};

// 1. extend namespace with a "utils" object
extend(myNS, { 
        utils:{
        }
});

console.log( "test 1" , myNS);
// myNS.utils now exists

// 2. extend with multiple depths (namespace.hello.world.wave)
extend(myNS, {
                hello:{
                        world:{
                                wave:{
                                    test: function(){
                                        //...
                                    }
                                }
                        }
                }
});

// test direct assignment works as expected
myNS.hello.test1 = "this is a test";
myNS.hello.world.test2 = "this is another test";
console.log( "test 2", myNS );

// 3. what if myNS already contains the namespace being added 
// (e.g. "library")? we want to ensure no namespaces are being 
// overwritten during extension

myNS.library = {
        foo:function () {}
};

extend( myNS, {
        library:{ 
                bar:function(){
                    //... 
                }
        }
});

// confirmed that extend is operating safely (as expected)
// myNS now also contains library.foo, library.bar
console.log( "test 3", myNS ); 


// 4. what if we wanted easier access to a specific namespace without having
// to type the whole namespace out each time?

var shorterAccess1 = myNS.hello.world;
shorterAccess1.test3 = "hello again";
console.log( "test 4", myNS);

//success, myApp.hello.world.test3 is now "hello again"

console.groupEnd();

Note

The above implementation is not cross-browser compatible for all objects and should be considered a proof-of-concept only. One may find the Underscore.js extend() method a simpler, more cross-browser-friendly implementation to start with: http://documentcloud.github.com/underscore/docs/underscore.html#section-67. Alternatively, a version of the jQuery $.extend() method extracted from core can be found here: https://github.com/addyosmani/jquery.parts.

For developers who are going to use jQuery in their applications, one can achieve the exact same object namespace extensibility with $.extend as follows:

// top-level namespace
var myApp = myApp || {};

// directly assign a nested namespace
myApp.library = {
  foo:function(){ 
    //...
  }
};

// deep extend/merge this namespace with another
// to make things interesting, let's say it's a namespace
// with the same name but with a different function
// signature: $.extend( deep, target, object1, object2 )
$.extend( true, myApp, {
    library:{ 
        bar:function(){
            //...
        }
    }
});

console.log("test", myApp); 
// myApp now contains both library.foo() and library.bar() methods
// nothing has been overwritten which is what we're hoping for.

For the sake of thoroughness, please see here for jQuery $.extend equivalents to the rest of the namespacing experiments found in this section.

Recommendation

Reviewing the namespace patterns we’ve explored in this section, the option that I would personally use for most larger applications is nested object namespacing with the Object Literal pattern. Where possible, I would implement this using automated nested namespacing, however, this is just a personal preference.

IIFEs and single global variables may work fine for applications in the small to medium range, however, larger code bases requiring both namespaces and deep subnamespaces require a succinct solution that promotes readability and scales. I feel this pattern achieves all of these objectives well.

I would also recommend trying out some of the suggested advanced utility methods for namespace extension, as they really can save time in the long run.

The best content for your career. Discover unlimited learning on demand for around $1/day.