JavaScript Tricks And Good Programming Style

Wednesday, August 9th, 2006 at 10:39 +0000 (UTC) by Alexander Kirk

Note that this is an updated version. Original version can be found here.

Thanks to the commenters I have updated this post with some better tricks.

In a loose series I'd like to point out a few of them. As I am currently mostly programming in JavaScript, I will write most of my samples in that language; also some of the tricks I mention only apply to JavaScript. But most of them apply to most programming languages around.

Optional parameter and default value #
When defining a function in PHP you can declare optional parameters by giving them a default value (something like function myfunc($optional = "default value") {}).

In JavaScript it works a bit differently:

var myfunc = function(optional) {
if (typeof optional == "undefined") {
optional = "default value";
}
alert(optional);
}

This is a clean method to do it. Basically I pretty much recommend the use of typeof operator.

update
Michael Geary (his comment) pointed out this solution that I like.


var myfunc = function(optional) {
if (optional === undefined) {
optional = "default value";
}
alert(optional);
}

The solutions mentioned (if (!optional), optional = optional || "default value", and the like) have problems when you pass 0 (zero) or null as an argument.

Commenters said that the 0/null problem is not one as this would not be the situation to use it. I would not say so. In an AJAX world where you do serialization back to a server/database often a 0/1 to false/true mapping has to be established. For default values it is important.

In case you just need to make sure that an object is not null I do prefer the mentioned

myobject = myobject || { animal: "dog" };

end update

Parameters Hints #
The larger your app gets, the more functions you get which you would use throughout the app. It also creates a problem with maintenance. As each function can contain multiple arguments it is not unlikely that you forget what those parameters were for (especially for boolean variables) or mix up their sequence (I am especially gifted for that).

So what I do is this: update substitute variables with comments end update

var myfunc2 = function(title, enable_notify) {
// [...]
}
myfunc2(/* title */ "test", /* enable_notify */ true);

This piece of code relies on the functionality of programming languages that the return value of an assignment is the assigned value. (This is something that you should also maintain in your app, for example with database storage calls, give the assignment value as a return value. It's minimal effort and you might be glad at some point that you did it).

If you do this you can see at any point in the code, what parameters the function takes. Of course this is not always useful, but especially for functions with many parameters it gets very useful.

Search JavaScript documentation #
When I need some documentation for JavaScript I use the mozilla development center (mdc). To quickly search for toLocaleString, I use Google: http://google.com/search?q=toLocaleString+mdc

As I am a German speaker I also use the excellent (though a bit out-dated) JavaScript section SelfHTML. I use the downloaded version on my own computer for even faster access.

The self variable #

update
… should be avoided. Even if someone like Douglas Crockford (creator of JSON) uses it and calls it that.

Let me quote Jack Slocum who put it best:

// used to fix "this" prob with Function.apply to give call proper scope
// nice method to put in your lib
function delegate(instance, method) {
return function() {
return method.apply(instance, arguments);
}
}

function Animal(name) {
this.name = name;
this.hello = function() {
alert("hello " + this.name);
}
}

var dog = new Animal("Jake");
var button = {
onclick : delegate(dog, dog.hello)
};
button.onclick();

I removed my code as it can be considered obsolete by this.
end update

Reduce indentation amount #

update
I have removed the code because it leads people into believing something different than I meant. So let me put it differently:

What I am opposing is white space deserts. If you have many levels of indentation then probably something is wrong.

If a for loop only applies to a handful of cases, don't indent the whole loop in an if clause but rather catch the other cases at the top.
Often it is advisable to move longer functionality to a function (there is a good reason for that name) that you call throughout a loop.
end update

That's all for now, to be continued. Further readings on this blog:

update
Eventhough some commenters disagreed with what I said, I think posts like this are very much needed in the bloggersphere. Even if they are not free of errors on the first take, great people can help improve them. I would appreciate if more people took that risk.
end update

, ,

42 Responses to “JavaScript Tricks And Good Programming Style”

  1. Ajaxian » JavaScript Tricks And Good Programming Style Says:

    [...] A wise man once said that one can never have too many Javascript hints and tricks. Well, okay - so maybe I made the man up, but the tips are real and there are some over in this new post from Alexander Kirk. I have been programming for about 10 years now, and I am always longing for improving my code. Throughout time I added a few habits that I consider to be good practices and increase the quality of my code. [...]

  2. Cody Swann Says:

    What version of safari will puke on your optional example? 2.x seems to work OK.

    Still, I like to use

    var myFunc = function(message)
    {
    message = message || 'HI THERE';
    ...
    }

  3. anjan bacchu Says:

    hi there,

    I know that you use wordpress but is there a way you can make your blog entries cleanly printable -- the only nice forum/blog site that I've seen which prints real clean is InfoQ -- I can just do a PRINT on the page wihtout printing the NAVIGATION bar on the side or any extraneous junk.

    br,
    `a

  4. Martin Bialasinski Says:

    I don't like the "Parameter Hints" method, because it pollutes the name space, as you wrote. Using an option object gives you the hints without this disadvantage, and it also make the sequence of parameters irrelevant.

    function myfunc2(options){

    }

    myfunc2({title: "test", enable_notify: true});

  5. Michael Says:

    In my experience, memory leaks are caused by referencing browser objects in a closure function. The only way to de-reference is to restart the browser.

  6. TI Says:

    RE: Parameters Hints

    I'm starting to get into an idea suggested by (I think Alex Russel) within the dojotoolkit.

    var myfunc2 = function(/*string*/ title, /*boolean*/ enable_notify) {
    // [...]
    }

    This feels overly verbose at times but I also work in Java so much of my time that it doesn't feel unfamiliar. You can also throw anything (keeping consistent) such as /* DOM Node */ as the def to help keep things clear.

  7. Phil Freo Says:

    In reference to "the self variable"... it is not a good idea to use the word "self" as the variable. Douglas Crockford himself uses "that" instead of "self" now, because self can be confused in some browsers as a synonym for the global "window" object.

  8. kourge Says:

    Like Cody said, it much more simple to use the boolean OR operator to stuff optional parameters with a default value.
    function myFunc(option) {
    option = option || "default value";
    }

    In Ruby, you can do it even easier:
    option ||= "default value"

    Doug Crockford also purposed ||= to be one of JavaScript's native operators.

  9. Steve Clay Says:

    A simple help in JS is to memorize which things are "falsy": http://simon.incutio.com/slides/2006/etech/javascript/js-reintroduction-notes.html#othertypes
    Really that whole article is reqd. reading.

  10. Mark Kahn Says:

    Personally I use a "prepare arguments" function in all my code. Put simply, it allows me to call a function with either the normal arguments or with an object containing them all, ie:

    myFunction(varA, varB, varC) OR myFunction({varA: 5, varB: 6, varC: 7})

    I find this to be very useful on functions that end up having a dozen arguments or whatnot. Each function simply has a call to the prepArgs function:

    var A = prepArgs(arguments, ['ns', 'ex', 'r', 'p', 'x'], ['ns'], _, 'load');if(!A) return;

    this has the list of variables (ns, ex, ...), the required variables (ns), arguments that must be arrays and the function name it came from (used for throwing an error). Arguments are then accessed with A.ns, etc instead of just "ns"

    function if anyone is interested, needs to be modified to work outside of my framework:

    function prepArgs(){
    var args = arguments[0];
    var nams = arguments[1];
    var reqs = arguments[2];
    var arys = arguments[3];
    var calf = arguments[4];

    if(args[0] && args[0].callee) return prepArgs(args[0]);

    var result = [], n, aN=isArray(nams);

    if(args[0] && args[0][0] && (args[0][0]===JSLibrary._$)){
    for(n in args[0][1]){
    result[n] = args[0][1][n];
    }
    if(aN){
    for(var i=nams.length-1; i>=0; i--){
    result[i] = result[names[i]];
    }
    }
    }else if(aN){
    for(var i=nams.length-1; i>=0; i--){
    result[nams[i]] = result[i] = args[i];
    }
    }else{
    for(var i=0; i=0; i--){
    if(!result[reqs[i]]){
    if(calf) throwError(JSLibrary.missingArgs.supplant({f: calf}));
    return false;
    }
    }
    }
    if(arys){
    for(var i=arys.length-1; i>=0; i--){
    result[arys[i]] = $A[result[arys[i]]];
    }
    }

    return result;
    }

  11. Jukka-Pekka Keisala Says:

    Good post, nice tips regarding indent and special thanks for mdc link. Mozilla Developer Center was one of the forgotten sites on my browser but now it's my top resource for JS.

  12. Alexander Kirk Says:

    Cody, kourge, Steve Clay: I've set an example up where I try to demonstrate the problems with your solution. Mostly it reduces to the fact that you cannot use zero (0) as valid parameter value: http://alexander.kirk.at/area7/2006/08/10/

  13. Alexander Kirk Says:

    anjan: Thanks for the notice. Is it better now?

  14. Michal Hantl Says:

    Nice post, but i dislike your indent sugestion. When you've got (or anybody else) 10 nested ifs, there is something wrong with your code.

  15. Alexander Kirk Says:

    Martin, Mark: this was what I considered, too. The downside of this is that now also the names of the arguments are prone to spelling errors. Also it adds quite some overhead.

    Michal: As I said, 10 levels is about other people's code. I like the indentation method for just 1 level of ifs. It's much cleaner.

    TI: this does look indeed more verbose but it achieves what I am trying without polluting the namespace. The difference between what you wrote and my point, is that I don't want to have type hinting, but rather a hint for the meaning :)

  16. cypher Says:

    Nice...

    Regarding your second trick, I guess prefixing your variable by something meaningful about its "type" should do the trick.

    In other words :
    myfunc2(title = "test", enable_notify = true);
    can be easily replaced by :
    myfunc2(strTest, blnEnableNotify);

    No much hassle involved...

  17. Doug Mayo-Wells Says:

    Cody, kourge, Steve Clay: I've set an example up where I try to demonstrate the problems with your solution.

    This was bugging me too. I'm now combining an options object with a typeof test for values that could reasonably be "falsey."
    http://geekgeekgeek.antithetical.org/2006/06/passing-false-when-the-default-is-true/

  18. shadytrees Says:

    Is there any downside to |optional == null| instead of |typeof optional = 'undefined'|? It wouldn't succumb to the 0 edge case or Safari's alleged deep, emotional issues.

  19. Ted Says:

    Personally I'm not a big fan of "continue" and "break". I think it obfuscates the flow of logic. Using your example above, why not do this:

    var arr = ["dog", "cat"];
    var action = 'greet';
    for(i = 0, ln = arr.length; i < ln; i++) {
    animal = arr[i];
    if (animal == "cat") alert("hello " + animal);
    }

    Here we have a one line if condition, so no extra indention is necessary. Plus without the "continue" the code is easier to read.

  20. Alexander Kirk Says:

    Ted, I have shortend the example. Of course in that case it would make more sense, not to use continue. But in a longer loop it might.
    As always with programming: don't religiously stick to such rules. Just be consistent.

    Also, for continue it could make more sense to define it positively and negate the whole thing. This is often easier to understand.

    if (!(animal == 'cat')) continue;

    PS: I hope it's ok for you that I have merged your comments into one.

  21. Alexander Kirk Says:

    shadytrees, your example works quite well, and I think it is mostly what you want. I have extended my sample page: http://alexander.kirk.at/area7/2006/08/10/
    As you can see, the only case that it handles kindof incorrectly is when you try to pass null as an argument. I agree that you would probably don't want to do that anyway, most of the time.
    Still the way I do it, provides a clean way to detect if the parameter was passed or not. In any case that I have tested so far.

  22. Brian Donovan Says:

    I just tried running "!null" in Safari 2.0.4 and got "true". Which version has the problem you mentioned? Also, another way to handle the named arguments thing is by passing a hash rather than a list of arguments. This is how Prototype does things:

    var myfunc2 = function(options) {
    // [...]
    }
    myfunc2({title: "test", enable_notify: true});

    The disadvantage here is that you have no explicit list in the function declaration and the extra {}. The advantage is that you don't pollute the global scope with lots of variables.

  23. Alexander Kirk Says:

    Brian, you are right. I am not able to reproduce it anymore ;) (if you read the comments above you can see that this is not the only benefit of the construct).
    I think I was referring to a difference between Firefox and Safari where you could write something in the form of if (!x) and it would work in Firefox but not Safari. I can't recall it now but I will post on my blog as soon as I come across it again.

  24. Michael Geary Says:

    Alex, about the "arg = arg || 'howdy';" or "arg = arg || { foo:123 };" notation... It's not really a problem that it doesn't work if arg is 0, because you don't use it in a situation like that. It's mostly useful for optional objects or arrays, so there is no ambiguity around the "false" value. It also can be useful for optional strings, as long as you're aware that "" will be treated as "false".

    About "self" vs. "that": What browser is confused by "self"? It is not a reserved word or anything magic. It is simply a property of the window object. In other words, the browser has done the equivalent of:

    window.self = window;

    If you use a variable named "self" inside a function, it should not be confused with this global "self". Perhaps Doug's choice of "that" is just to avoid *programmer* confusion?

    About the "typeof foo == 'undefined'", I prefer doing this:

    // At the top of the .js file, to support very old browsers
    window.undefined = window.undefined;

    Now you can code this in any browser:

    if( foo === undefined ) whatever;

    In many cases, however, you don't really need to distinguish between null and undefined values, so this is sufficient:

    if( foo ==null ) whatever;

  25. The Inside Of My Head » links for 2006-08-10 Says:

    [...] JavaScript Tricks And Good Programming Style Good practice to increase the quality of your code. (tags: webdev javascript) [...]

  26. [[ the sirens of titan ]] » Blog Archive » links for 2006-08-11 Says:

    [...] alexander kirk » Blog Archive » JavaScript Tricks And Good Programming Style some tips on good practice for javascript programming. (tags: javascript programming article) Posted by geekfreak Filed in Bookmarks [...]

  27. Jack Slocum Says:

    While I applaud the effort trying to enlighten newer developers, I hope no one who ever works on code around me follows these tips. Every code block in every tip is bad. I know that sounds harsh, but it's true.

    Tip 1 - Optional parameter and default value: "This is a clean method to do it. "
    Not true, that's a hideous way to do it. It's ok for one parameter, but if you had more than one your functions would start with multiple if + typeof statements and quickly become ugly. Fun you complain about indentation, but have no problem with multiple ifs and typeofs.

    Tip 2 - Parameters Hints:
    Talk about polluting the global namespace with unneeded variables. The proper way to do parameter hints:

    myfunc2(/*title*/ "test", /*enable_notify*/ true);

    Tip 3: Ok this one isn't bad. People should definitely read the docs.

    Tip 4 - The self variable
    "self" is a global variable already in Internet Explorer. Either way the code written is bad anyway. That is hack to get around writing bad javascript code. I have seen it all different way including the popular var _this = this.
    The proper way to do what you wanted:

    // used to fix "this" prob with Function.apply to give call proper scope
    // nice method to put in your lib
    function delegate(instance, method) {
      return function() {
        return method.apply(instance, arguments);
      }
    }

    function Animal(name) {
      this.name = name;
      this.hello = function() {
        alert("hello " + this.name);
      }
    }

    var dog = new Animal("Jake");
    var button = {
      onclick : delegate(dog, dog.hello);
    };
    button.onclick();

    Tip 5 - Reduce indentation amount:
    This is the worst tip of all. You are actually suggesting people to write ugly code. Common coding practices exist for a reason. People indent for a reason. These things make code more readable and easier to maintain. Control structures are indented to make it obvious where you are in the flow. Continue and break statements destroy that and should be used sparingly.

    What's funny is the example code block you gave is the recommended standard for Java and C#. It is a common format almost all developers will be used to.

    From the code and recommendations in this article I'd put your personal Javascript level as a 5 on a scale of 10. I'd devote more time to reading tutorials rather than writing them.

  28. JT... Says:

    Jack

    var button = {
    onclick : delegate(dog, dog.hello);

  29. Jack Slocum Says:

    Actually it's:
    var button = {
    onclick : delegate(dog, dog.hello)
    };

    I just edited the posted example and didn't see the extra semi-colon.

  30. Micah Goulart Says:

    How about this for the loop?

    var arr = ["dog", "cat"];
    for(i = 0, ln = arr.length; i

  31. Dean Edwards Says:

    I agree with Jack Slocum. Some of these tips are just plain bad. Complete your own learning before you presume to teach.

  32. Andrea Martines Says:

    I disagree with Jack and Dean: even when a post from an untrusted source contains some bad practices, often the right way is pointed out in some of the comments, together with interesting links. So, this kind of discussions enrich anyway my knowledge. And it's also useful in order to distinguish between trusted (as Dean's blog) and untrusted sources.

  33. Webmaster Libre » Archivo del weblog » Adelgazar firefox y buenas prácticas para Javascript Says:

    [...] de 2006 a las 13:51 y está archivada en: Enlaces. Puedes dejar un comentario, o enviar un trackback desde tusitio. [...]

  34. alexking.org: Blog > Around the web Says:

    [...] alexander kirk - JavaScript Tricks And Good Programming Style [...]

  35. Alexander Kirk Says:

    Jack, Dean: Thanks for the input. I have clarified some of my points in my post. Eventhough you seem to disagree with most of what I say I think it is important to lean out of the window in order to get things right.

  36. Tim Down Says:

    Michael Geary said:

    "Now you can code this in any browser:

    if( foo === undefined ) whatever;"

    Now, this is unfortunately not true. IE5 on Windows does not support undefined as an object (the error you get tells you that in fact 'undefined' is undefined). There may be other browsers that don't support it. IE5 on Windows is enough on its own to put me off, given that I still have to write code to work on it.

  37. Tim Down Says:

    ... and now I've read the rest of Michael's post, saw

    window.undefined = window.undefined;

    ... and retract everything I just said. Sorry everyone.

  38. Peter Says:

    I like wordpress, but think that they have missed a few things. I have started to use Joomla instead. It gives me more features.

    But thats just my opinion....

  39. JavaScript Tricks And Good Programming Style » Dee’s-Planet! Blog Says:

    [...] Optional parameter and default valueParameters HintsSearch JavaScript documentationThe self variableReduce indentation amount | read here in detail… [...]

  40. Jack Slocum’s Blog » Blog Archive » Getting Started - Making the scripts from this site work Says:

    [...] The second function createDelegate() is even more useful. It is used to solve the problem of what the scope (“this”) is set to in a method call. I’ll use a modified version of the example I previously gave on Alexander Kirk’s blog: [...]

  41. links for 2006-08-10 at willkoca Says:

    [...] alexander kirk » Blog Archive » JavaScript Tricks And Good Programming Style (tags: javascript) [...]

  42. inner.geek » Blog Archive » My del.icio.us bookmarks this …. year? Says:

    [...] alexander kirk » Blog Archive » JavaScript Tricks And Good Programming Style (tagged: code coding design dev free javascript js learning method programming reference scripting software standards style teaching tech tools tips web web2.0 webdesign webdev bestpractices) [...]