Tuesday, October 28th, 2008

How to structure your JavaScript code

Category: JavaScript, Library

<>p>Peter Michaux has shared how he structures his code these days, as he has settled on a pattern:

The code example below is a simple little logger widget. It appends messages to a list and has a clear link to delete all the recorded messages.

javascript
< view plain text >
  1. // Wrap code with module pattern.
  2. (function() {
  3.   var global = this;
  4.  
  5.   // helper functions
  6.   var sanatize = function(msg) {
  7.     return (String(msg)).replace('&', '&amp;').replace('< ', '&lt;').replace('>', '&gt;');
  8.   };
  9.  
  10.   // widget constructor function
  11.   global.MY_makeLogger = function() {
  12.  
  13.     // private instance methods    
  14.     var clear = function() {
  15.       LIB_emptyElement(logEl);
  16.     };
  17.     var append = function(msg, className) {
  18.       LIB_insertBottom(logEl,
  19.         '<dt class="'+className+'">' + (new Date()) + '</dt>' +
  20.         '<dd class="'+className+'">' + sanatize(msg) + '</dd>');
  21.     };
  22.  
  23.     // create widget DOM fragment
  24.     var parser = document.createElement('div');
  25.     LIB_upateElement(parser,
  26.       '<div class="Logger">' +
  27.         '<p><a href="#" class="clearLink">clear</a></p>' +
  28.         '<dl class="log"></dl>' +
  29.       '</div>');
  30.  
  31.     // find pieces and enliven DOM fragment
  32.     var rootEl = LIB_find('.Logger', parser)[0];
  33.     parser = null; // enable garbage collection of parser div
  34.     var logEl = LIB_find('.log', rootEl)[0];
  35.     LIB_on(LIB_find('.clearLink', rootEl), 'click', function(e) {
  36.         LIB_preventDefault(e);
  37.         clear();
  38.     });
  39.  
  40.     // public instance methods
  41.     return {
  42.       getRootEl: function() {return rootEl;},
  43.       log      : function(msg) {append(msg, 'log'  );},
  44.       warn     : function(msg) {append(msg, 'warn' );},
  45.       error    : function(msg) {append(msg, 'error');}
  46.     };
  47.  
  48.   };
  49. })();

He talks about how he uses naming conventions to keep track of library functions, exported functions, gensym ids.

Related Content:

Posted by Dion Almaer at 9:39 am
18 Comments

++---
2.9 rating from 50 votes

18 Comments »

Comments feed TrackBack URI

It’s always great to see how other programmers code. Thanks!

Is “global” really global? What I would expect from a variable called global (in a JavaScript program) is an object that hold global variables for the use of the entire program. (I’m in the habit of “var that=this;” instead, mostly because I see other people use it so much.)

The misspelling of sanitize drives me crazy. If I recall correctly, on the Atari ST, all the color functions had the word “palette” misspelled as “pallete.” Every time I got to one of those functions I had to slow down, grit my teeth, and think.

Comment by Nosredna — October 28, 2008

I’m a fan of the module pattern. I use it so often to get so much done I don’t understand the need for classical inheritance in javascript.

Comment by ilazarte — October 28, 2008

I’ve always used objects as namespaces. He says that’s bad, but doesn’t elaborate. Any idea what he’s talking about?

Comment by ajaxianreader123 — October 28, 2008

@Nosredna-
“var global = this” when wrapped inside the outer “(function(){ … })();” construct will by default make global (and this) point to the global “window” object, which is global and in scope of everything in JS on a webpage. This happens because a function by default runs in the context of “window” unless otherwise bound, so that outer anonymous function call does automatically bind its “this” to “window”.

Comment by shadedecho — October 28, 2008

@shadedecho,
.
Oh, I hadn’t thought of that. Nifty.

Comment by Nosredna — October 28, 2008

What I don’t understand is why why people tend to use this “var global = this; this.foo = …” construct or just “this.foo = …” and not just “window.foo = …”.

Is there any benefit of *not* using “window.foo = …” ?

Comment by fforw — October 28, 2008

Because you assume window is the global name. If you move your code into a different environment, it won’t break when it assumes.

Comment by TNO — October 28, 2008

correction: won’t -> will

Comment by TNO — October 28, 2008

@fforw:
The module pattern allows you to place the module (its globals and everything) within whatever context you like. If you run it like: “(function(){ var global=this; …})();”, then global will be assigned to window. But you can also do this:
(function(){ var global=this; …}).call(My.namespace);
… and then, the global inside will be assigned to My.namepace. This pattern is nice because it allows for changing the context without changing the code much.

Comment by pianoroy — October 28, 2008

I’ve never been a fan of the “prefix-as-a-namespace” naming style. There are a variety of reasons for this that I’d like to hear Mr. Michaux’s thoughts on.

1. “dot” notation is (for me) much more intuitive when dealing with nested namespaces. For example, “new acme.widgets.Logger()” just reads better than “new acme_widgets_Scroller()”. The latter creates a cognitive clash between two nameing conventions I’ve long since grown accustomed to. “_” separated identifiers tend to be all lower (or upper) case, but in JavaScript the convention is to capitalize Class names but not generic objects. Thus, it just looks wierd.

2. Dot-notation allows you to make convenience aliases to any node in the namespace hierarchy. For example, if I’m going to be referring to various members of the Logger class, I can do “var logr = acme.widgets.Logger” inside my function, and then refer to “logr.foo”. It’s easier to type and more compact.

3. Dot-notation allows for a variety of shortcuts when constructing nested APIs. For example, you can use the literal object syntax to build a nested API like this:

var acme = {
widget: {
Scroller: function () {
}
}
}

4. Dot-notation is easier to type. You young pups may not care, but as a professional programmer of 20+ years, my hands and wrists will feel the difference between a “_” convention where I have to use SHIFT all the time, and a “.” convention where I don’t, after a long day of coding.

“Now get off my lawn!” ;-)

Comment by broofa — October 28, 2008

I stopped reading after the silly “sanatize” (!) function that encodes first occurences of [&<>].

Comment by nbr — October 29, 2008

Not only do I agree with broofa here on the semantic side of things, but there is also a practical consideration to not use the module pattern as stated by Peter Michaux:
When you’re using a JS framework to aid in developing your webapp, like everyone does or should do nowadays, the namespace provides a hook for your custom code to be attached to a single global scope like this:

// in case you're using Javeline Platform. Imagine YUI, jQuery or
// anything else if you prefer them...
jpf.myApp.func1 = function() {
var appy = jpf.myApp; // one lookup
// ...do stuff...
};

This saves us a lookup, because global lookups are expensive.
The declaration of ‘myApp’ during the script initialization costs one lookup, but that is no different than the module-approach.

Additionally, the ‘(function(global){})(this);’ syntax is not necessarily bad as it provides access to the global scope in exotic JS engines like screenreaders or standalone spidermonkey/ Rhino, but it forces a function call during script initialization, which will slow down the startup of your app. Using this method to declare your own namespace globally, shows another advantage of using a namespace:

(function(global){
// declaring your namespace here saves you from using the
// (function(global){})(this); struct anywhere else.
global.jpf = {
// stuff...
};
})(this);

Another thing I noticed when looking at Peter’s code, is his ‘LIB_updateElement()’ statement, which contains a string, concatenated with the ‘+’ operand. It works, but is unnecessarily slow. I suggest using the following syntax whenever and wherever possible:

// Delimiting strings with a backslash '\' saves you a string concat and
// still allows you to format your code within 80 characters with
// indenting:
LIB_upateElement(parser,
'\
clear\
\
');

With all the research flying around, I’d recommend this syntax to all.

I don’t have a test page with raw metrics online somewhere, but I can put one up if someone feels I should (again :P) prove my aforementioned statements.

Comment by mikedeboer — October 29, 2008

Wow, the comment formatter totally screwed up parts of my code blocks..
Take 2:

// Delimiting strings with a backslash '\' saves you a string
// concat and still allows you to format your code within
// 80 characters with indenting:
LIB_upateElement(parser,
'<div class="Logger">\
<p><a href="#" class="clearLink">clear</a></p>\
<dl class="log"></dl>\
</div>');

Comment by mikedeboer — October 29, 2008

Thanks for reminding us to use the backslash. I do have a couple places in my code where I was doing a concat.

Comment by Nosredna — October 29, 2008

> 1. “dot” notation is (for me) much more intuitive when dealing with nested namespaces…Thus, it just looks wierd.

Why is dot notation more intuative? Because some other languages use it for namespacing? Since JavaScript identifiers don’t usually use underscore, it is available for some other meaning like namespacing. Using a dot costs as property lookup in JavaScript.

> 2. Dot-notation allows you to make convenience aliases to any node in the namespace hierarchy. For example, if I’m going to be referring to various members of the Logger class, I can do “var logr = acme.widgets.Logger” inside my function, and then refer to “logr.foo”. It’s easier to type and more compact.

I can do “var foo = acme_widgets_Logger_foo;” and then just refer to “foo” which is less characters and executes faster. I’m not worried about “this” as I don’t use it in API functions.

> 3. Dot-notation allows for a variety of shortcuts when constructing nested APIs. For example, you can use the literal object syntax to build a nested API like this:
>
> var acme = {
> widget: {
> Scroller: function () {
> }
> }
> }

I don’t see this as a significant advantage. It isn’t so hard to write

acme_Scroller = function (){}

I don’t think there is any need for a middle “widget” namespace.

> 4. Dot-notation is easier to type.

I don’t end up typing underscore as often as you might think.

Comment by PeterMichaux — October 29, 2008

Thank you.

Comment by Kimson — October 29, 2008

FAIL @ spelling of ‘sanatize’

Comment by shiftb — October 30, 2008

FAIL @ spelling of ‘intuative’ :)
.
but, grammer (d’oh!) aside, abusing the module pattern is a bad idea. It may be good for small pieces of code (like a short snippet with a couple of sub-functions and variables as a means to not pollute the outer namespace) but it’s completely useless if you intend to use it to encapsulate your public API.
.
Just the fact that each time you need to add a public function/property you have to repeat yourself by writing both the “internal” implementation and it’s interface completely throwing the dynamic part of the language out the window, makes me wonder why you are coding in javascript and not C++ or Java.

Comment by gonchuki — November 14, 2008

Leave a comment

You must be logged in to post a comment.