Blog

JavaScript Design Patterns Used in Smart Layers

JavaScript design patterns are important for the maintainability and scalability of web applications. For Smart Layers, we focused on writing DRY (Don’t Repeat Yourself), consistent, and cross-browser compliant code. Before we talk about the specific techniques that we used, let’s first understand the Smart Layers use case.

Use Case

Smart Layers is a product suite that currently includes 6 different UI widgets (including share, follow, recommended content). Although each widget contains unique functionality, there are many similarities between the widgets. For example, all widgets create and append DOM elements, listen to events, support user-specified options, utilize CSS3 show/hide animations, etc. Once we put our code architect hats on, we realized that we needed an inheritance model that could handle both unique widget functionality and common logic without code duplication.

Simple JavaScript Prototypal Inheritance

At this point, it is important to delve into object-oriented JavaScript, and recognize the difference between instance properties and prototype properties.

Instance Properties

Instance properties are properties that are scoped to a particular instance object and are not propagated to other instances (unless you go out of your way to change that). Let’s look at an example:

  // Creates an AddThisEmployee constructor function
  function AddThisEmployee(name) {
    this.name = name;
  };

  // Creates an AddThisEmployee instance object
  // and stores it in the greg local variable
  var greg = new AddThisEmployee('Greg Franko'),
    // Creates an AddThisEmployee instance object
    // and stores it in the sol local variable
    sol = new AddThisEmployee('Sol Chea');

  // Greg doesn't like his name and decides to
  // legally change it to 'Frank The Tank'
  greg.name = 'Frank The Tank';

  // Sol has no idea that Greg has changed his name to
  // 'Frank The Tank' and continues to call him Greg
  // This angers Greg

In the previous example, each instance object contains one instance property (name). Since each instance object’s properties are not propagated to each other, they are not aware of each other’s changes.

Prototype Properties

On the other hand, prototype properties are properties that are propagated to all instances. In JavaScript, an object’s property is looked up at run time, by first checking if there is an instance property. If there is not an instance property, then the object’s prototype is checked next to see if it contains the property. It is important to remember that if a prototype property changes, all instances that use that prototype property are propagated to reflect that new value. In other words, all instance prototype properties are kept in sync.

Let’s demonstrate prototypes by expanding upon our previous code example:

  // Creates a Person constructor function
  function AddThisEmployee(name) {
    this.name = name;
  };

  // Assigns the AddThisEmployee prototype property
  AddThisEmployee.prototype = {
    companyName: 'AddThis'
  };
  // Creates an AddThisEmployee instance object and
  // stores it in the greg local variable
  var greg = new AddThisEmployee('Greg Franko'),
    // Creates an AddThisEmployee instance object and
    // stores it in the sol local variable
    sol = new AddThisEmployee('Sol Chea');

  // Greg doesn't like his name and decides to
  // legally change it to 'Frank The Tank'
  greg.name = 'Frank The Tank';

  // Sol has no idea that Greg has changed his name to
  // 'Frank The Tank' and continues to call him Greg
  // This angers Greg

  // AddThis changes it's company name to 'Team Awesome'
  AddThisEmployee.prototype.companyName = 'Team Awesome';

  // Both Greg and Sol know that AddThis changed it's name
  console.log(greg.companyName);
  console.log(sol.companyName);

For Smart Layers, every widget instance reuses the same object as it’s prototype. This both simplifies the codebase and makes it easy to make changes that need to be propagated to all widgets.

Factory Pattern

Since every widget is created in a similar way, we needed a design pattern to take care of the instance creation process. Smart Layers uses the Factory pattern as a generic interface for creating instance objects.

Here’s a high-level look:

  var commonObj = {
    exampleProp: 'Each instance will have this property';
  },
    factory = function(name, instanceProps) {
      var Constructor = function() {
        for (var x in instanceProps) {
          if (instanceProps.hasOwnProperty(x)) {
            this[x] = instanceProps[x];
          }
        }
      },
        widget;
      widget.prototype = commonObj;
      widget = new Constructor();
      widget.name = name;
    };

  // Calls the factory method
  factory('share', {
    // Constructor function
    _create: function() {}
  });

The previous example shows a factory() method that can be called by passing a string name and an object literal containing all of the desired instance properties. This abstracts away the JavaScript constructor and prototype assigning ugliness, and provides an easy way to implement a consistent API for each instance. For similar techniques that work with jQuery plugins, check out the jQueryUI Widget Factory and jqfactory open-source projects.

Module Pattern

Another major design pattern used in Smart Layers is the module pattern. Currently, the Smart Layers JavaScript API only has one public method (destroy) that can be used after Smart Layers is initialized on the page. Under the hood though, the JavaScript API consists of multiple hidden methods that are used internally. To accomplish public/private API methods, we took advantage of JavaScript function scoping to only expose what we wanted to.

Let’s look at a basic example of the module pattern:

  // Function constructor
  var Person = function(obj) {
    // Local variables that are privately scoped
    var firstName = obj.firstName,
      lastName = obj.lastName,
      occupation = obj.occupation;

    // Public API properties
    return {
      firstName: firstName
    }
  },
  // Instantiating a new Person instance object
  greg = new Person({
    firstName: 'Greg',
    lastName: 'Franko',
    occupation: 'JavaScript Engineer'
  });

  // Prints out "Greg"
  console.log('first name', greg.firstName);

  // Prints out "undefined"
  console.log('last name', greg.lastName);

  // Prints out "undefined"
  console.log('occupation', greg.occupation);

This example demonstrates that all local variables, declared using the var keyword, are only accessible within the declaring function or other nested functions. In our example, a person’s first name, last name, and occupation are all local variables. An object, that includes the firstName property, is then returned inside of our constructor function. This demonstrates a closure, since a local property is kept alive and can be referenced inside of a different scope (when we try to retrieve greg.firstName). Since the firstName local variable is the only variable returned, it is the only property that can be retrieved from a different scope.

Here’s a more practical example:

  // Method that will only return functions that do not
  // begin with an underscore
  function publicApi (api) {
    var method,
      currentMethod,
      publicApi = {};
    for(method in api) {
      currentMethod = api[method];
      if(method.charAt(0) !== '_') {
        publicApi[method] = api[method];
      }
    }
    return publicApi;
  }

  // Stores the return value of the publicApi method
  var publicMethods = publicApi({
    publicMethod: function() {},
    _privateMethod: function(){}
  });

  // Should only contain the publicMethod as a property
  console.log('Public API', publicMethods);

This demonstrated the technique Smart Layers uses to expose public API methods. All methods that start with an underscore are assumed to be private and are not returned as a public method.

Prototypal inheritance, the factory pattern, and the module pattern are all extremely useful techniques. I’d love to hear your thoughts and the patterns you use in your own JavaScript code bases. Comment below!

  • There are somethings that need to be added and worked on with smart layers.

    1. The Recommended layer needs to be able to be placed wherever we choose OR beneath the post. Placing them at the very end of the page makes it look bad, and it can ruin the design.

    2. The follow layer, needs the ability to open down and not left. If it opened down we could use all of the buttons with out running into our content, even when it opens it looks bad.

    3. What’s next layer needs to have a customized version one with only links and one like it is now. The reason is while images are great and do increase the chances of someone going to an article they are generally large and intersect with the content. These extra links give them options, and allows them to get more options. This way they are more likely to click the article since the chances of finding one more appealing is greater.

    4. Add a follow button for Stumbleupon..

    5. Allow a follow button for Linkedin groups.

    6. Add a follow button for Myspace. While not popular some people are still using it.

    7. Smart layers recommended layer needs to have smaller images, and should show more.

    8. It needs better integration with the welcome bar, it needs to be on top of it by default.

    9. Why not give us a replacement of the trending content widget, and instead give us a layer that is a bar on the top of the page (works with the follow layer) and this layer will show the posts as a marquee. Then we have or follow layer right there as well.

    10. Work on the load time of smart layers.

  • We really appreciate the feedback Scott. A lot of your recommendations are currently being worked on, so we’ll post an update when everything is done. Thanks again.

  • My
    addthis toolbar is not working…it’s try to open the social site..says
    connecting to fb or reddit and nothing happens…my site is http://www.thesagenext.com

  • Hi, sorry we missed your message earlier. Can you give us more details about the problem you’re seeing?

\n\n