JavaScript Tricks And Good Programming Style

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

javascript, tricks, coding practices

35 thoughts on “JavaScript Tricks And Good Programming Style

  1. 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

  2. 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.

  3. 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;
    }

  4. 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 :)

  5. 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…

  6. 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.

  7. 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.

  8. shadytrees, your example works quite well, and I think it is mostly what you want. I have extended my sample page: http://alex.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.

  9. 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.

  10. 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.

  11. 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;

  12. 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.

  13. 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.

  14. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.