Sunday 7 April 2013

Javascript Folding Pattern

There are many patterns for arranging code in Javascript, such as the Module Pattern, the Revealing Module Pattern and the Prototype Pattern. Most programmers adopt their own style based on these. After creating a project with a lot of interconnected Javascript, I slowly derived this pattern.

The Folding Pattern is aware that Javascript is a functional/imperative object-oriented language that employs object prototypes rather than classes. It allows for the concept of private members, which is of importance in large projects (to combat accidental mangling of an object). By sticking to the rigorous formatting you can create very large objects which are easy to maintain and navigate. (Especially if you use an IDE which allows for folding of statement blocks.)

Here are two templates (copy them and alter as required) of how to use the Folding Pattern, one is a singleton and the other is a type:

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Folding Pattern - templates
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(function (global) {
   "use strict";

   // Create a name-space in the global space.

   var foldingPattern = global.foldingPattern = global.foldingPattern || {};

   //====================================
   // The template for making a singleton.
   //====================================
   foldingPattern.singletonTemplate = (function () {
       var N = {}, // Enclosed (private) members are here.
           X = {}; // Exposed (public) members are here.

       (function ENCLOSED_FIELDS() {
           N.myField = 0;
       }());

       (function ENCLOSED_METHODS() {
           N.myMethod = function () {
               N.myField++;
           };
       }());

       (function EXPOSED_PROPERTIES() {
           Object.defineProperty(X, 'myProperty', {
               get: function () {
                   return N.myField;
               }
           });
       }());

       (function EXPOSED_METHODS() {
           X.myMethod = N.myMethod;
       }());

       // Return the exposed object instance.
       return X;
   }());
   //====================================


   //====================================
   // The template for making a type.
   //====================================
   // This function is used to segregate the components of the type.
   (function () {
       // Here is the constructor section.
       var thisType = foldingPattern.TypeTemplate = function () {
           var N = {}, // Enclosed (private) members are here.
               X = this; // Exposed (public) members are here.

           (function ENCLOSED_FIELDS() {
               N.myField1 = 0;
           }());

           (function EXPOSED_FIELDS() {
               X.myField2 = 0;
           }());

           // The properties below have access to the enclosed fields.
           // Careful with functions exposed within the closure of the
// constructor, each new instance will have it's own copy.
           (function EXPOSED_PROPERTIES_WITHIN_CONSTRUCTOR() {
               Object.defineProperty(X, 'myProperty1', {
                   get: function () {
                       return N.myField1;
                   },
                   set: function (value) {
                       N.myField1 = value;
                   }
               });
           }());
       };

       // Here is the prototype section.
       (function PROTOTYPE() {
           var P = thisType.prototype;

           (function EXPOSED_PROPERTIES_WITHIN_PROTOTYPE() {
               Object.defineProperty(P, 'myProperty2', {
                   get: function () {
                       return this.myField2;
                   }
               });
           }());

           (function EXPOSED_METHODS() {
               P.myMethod = function () {
                   return this.myProperty1 + this.myField2;
               };
           }());
       }());
   }());
   //====================================

}(this));
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


To use the templates just take a copy of one and rearrange it to suit. Here are two examples of using the templates:

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Folding Pattern - examples
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(function (global) {
   "use strict";

   var foldingPattern = global.foldingPattern = global.foldingPattern || {};

   //====================================
   // An example of making a singleton.
   //====================================
   foldingPattern.singletonExample = (function () {
       var N = {}, // Enclosed (private) members are here.
           X = {}; // Exposed (public) members are here.

       (function ENCLOSED_FIELDS() {
           N.text = 'untweaked';
           N.count = 0;
           N.nums = [1, 2, 3];
       }());

       (function ENCLOSED_METHODS() {
           // An enclosed instance method.
           N.tweak = function () {
               N.count++;
               N.incrementNumbersByCount();
               N.text = 'tweaked';
           };

           // An enclosed instance method that uses exposed properties.
           N.incrementNumbersByCount = function () {
               var i;
               for (i = 0; i < X.numbers.length; i++) {
                   X.numbers[i] += N.count;
               }
           };
       }());

       (function EXPOSED_PROPERTIES() {
           Object.defineProperty(X, 'numbers', {
               get: function () {
                   return N.nums;
               }
           });

           Object.defineProperty(X, 'tweakStatus', {
               get: function () {
                   return N.text;
               },
               set: function (value) {
                   N.text = value;
               }
           });
       }());

       (function EXPOSED_METHODS() {
           // Expose an enclosed function.
           X.tweak = N.tweak;
       }());

       // Return the exposed object instance.
       return X;
   }());

   (function () {
// Here is how to use that singleton.

 var mySingRef = foldingPattern.singletonExample;
mySingRef.tweak();
mySingRef.tweakStatus = 'dunno';
   }());
   //====================================


   //====================================
   // An example of making a type.
   //====================================
   // This function is used to segregate the components of the type.
   (function () {
       // Here is the constructor section.
       var thisType = foldingPattern.TypeExample = function () {
           var N = {}, // Enclosed (private) members are here.
               X = this; // Exposed (public) members are here.

           (function ENCLOSED_FIELDS() {
               N.toggle = false;
               N.text = '';
           }());

           (function EXPOSED_FIELDS() {
               X.count = 0;
               X.numbers = [1, 2, 3];
           }());

           // The properties below have access to the enclosed fields.
           // Careful with functions exposed within the closure of the
// constructor, each new instance will have it's own copy.
           (function EXPOSED_PROPERTIES_WITHIN_CONSTRUCTOR() {
               Object.defineProperty(X, 'toggle', {
                   get: function () {
                       N.toggle = !N.toggle;
                       return N.toggle;
                   }
               });

               Object.defineProperty(X, 'text', {
                   get: function () {
                       return N.text;
                   },
                   set: function (value) {
                       N.text = value;
                   }
               });
           }());
       };

       // Here is the prototype section.
       (function PROTOTYPE() {
           var P = thisType.prototype;

           (function EXPOSED_PROPERTIES_WITHIN_PROTOTYPE() {
               Object.defineProperty(P, 'numberLength', {
                   get: function () {
                       return this.numbers.length;
                   }
               });
           }());

           (function EXPOSED_METHODS() {
               P.incrementNumbersByCount = function () {
                   var i;
                   for (i = 0; i < this.numbers.length; i++) {
                       this.numbers[i] += this.count;
                   }
               };
               P.tweak = function () {
                   if (this.toggle) {
                       this.count++;
                   }
                   this.text = 'tweaked';
               };
           }());
       }());
   }());

   (function () {
// Here is how make an instance of that type.
    var myInst = new foldingPattern.TypeExample();
myInst.text = 'toggle: ' + myInst.toggle;
    myInst.tweak();
   }());
   //====================================

}(this));
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~



This code is completely free to use by anybody. It is held under the Do What You Want To Public License: http://tinyurl.com/DWYWTPL


[Edit]
Regarding the comment below, to explain further, the main reason for the self-executing functions (in upper-case) is because many text editors allow for "code folding". So for a large object definition, the methods can be folded up into a single line. Somewhat like this:

   // This function is used to segregate the components of the type.
   (function () {
       // Here is the constructor section.
       var thisType = foldingPattern.TypeExample = function () {
           var N = {}, // Enclosed (private) members are here.
               X = this; // Exposed (public) members are here.

           +(function ENCLOSED_FIELDS() {...

           +(function EXPOSED_FIELDS() {...

           // The properties below have access to the enclosed fields.
           // Careful with functions exposed within the closure of the
// constructor, each new instance will have it's own copy.
           +(function EXPOSED_PROPERTIES_WITHIN_CONSTRUCTOR() {...

       };

       // Here is the prototype section.
       (function PROTOTYPE() {

           var P = thisType.prototype;

           +(function EXPOSED_PROPERTIES_WITHIN_PROTOTYPE() {...

           +(function EXPOSED_METHODS() {...
       }());
   }());

This makes it easier to see the file in a kind of "overview mode", where moving between sections is made easier and more manageable.

2 comments:

  1. I don't see the value of surrounding each function definition with a self-executed anonymous function (SEAF). Can't the same effect be obtained by using only one outer SEAF to hide all identifiers except the public ones (such as the constructor)? That seems to work fine for me.

    ReplyDelete
  2. Whatever works for your team is obviously best. The reason for the extra SEAF's is to compartmentalise different conceptual members. I can see this would be overkill for some.

    ReplyDelete