JavaScript Tricks And Good Programming Style
Wednesday, August 9th, 2006 at 10:39 +0000 (UTC) by Alexander KirkNote 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
August 9th, 2006 at 22:28 +0000 (UTC)
[...] 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. [...]
August 10th, 2006 at 00:00 +0000 (UTC)
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';
...
}
August 10th, 2006 at 00:32 +0000 (UTC)
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
August 10th, 2006 at 00:53 +0000 (UTC)
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});
August 10th, 2006 at 01:49 +0000 (UTC)
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.
August 10th, 2006 at 02:03 +0000 (UTC)
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.
August 10th, 2006 at 03:04 +0000 (UTC)
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.
August 10th, 2006 at 04:30 +0000 (UTC)
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.
August 10th, 2006 at 05:09 +0000 (UTC)
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.
August 10th, 2006 at 05:15 +0000 (UTC)
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;
}
August 10th, 2006 at 07:56 +0000 (UTC)
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.
August 10th, 2006 at 08:57 +0000 (UTC)
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/
August 10th, 2006 at 09:15 +0000 (UTC)
anjan: Thanks for the notice. Is it better now?
August 10th, 2006 at 09:26 +0000 (UTC)
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.
August 10th, 2006 at 09:34 +0000 (UTC)
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 :)
August 10th, 2006 at 13:05 +0000 (UTC)
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...
August 10th, 2006 at 13:09 +0000 (UTC)
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/
August 10th, 2006 at 15:03 +0000 (UTC)
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.
August 10th, 2006 at 15:09 +0000 (UTC)
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.
August 10th, 2006 at 15:14 +0000 (UTC)
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.
August 10th, 2006 at 15:22 +0000 (UTC)
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.
August 10th, 2006 at 15:39 +0000 (UTC)
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.
August 10th, 2006 at 15:52 +0000 (UTC)
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.
August 10th, 2006 at 23:01 +0000 (UTC)
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;
August 11th, 2006 at 09:53 +0000 (UTC)
[...] JavaScript Tricks And Good Programming Style Good practice to increase the quality of your code. (tags: webdev javascript) [...]
August 11th, 2006 at 10:18 +0000 (UTC)
[...] 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 [...]
August 11th, 2006 at 11:44 +0000 (UTC)
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.
August 11th, 2006 at 15:57 +0000 (UTC)
Jack
var button = {
onclick : delegate(dog, dog.hello);
August 11th, 2006 at 21:42 +0000 (UTC)
Actually it's:
var button = {
onclick : delegate(dog, dog.hello)
};
I just edited the posted example and didn't see the extra semi-colon.
August 12th, 2006 at 06:37 +0000 (UTC)
How about this for the loop?
var arr = ["dog", "cat"];
for(i = 0, ln = arr.length; i
August 12th, 2006 at 14:32 +0000 (UTC)
I agree with Jack Slocum. Some of these tips are just plain bad. Complete your own learning before you presume to teach.
August 13th, 2006 at 10:47 +0000 (UTC)
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.
August 13th, 2006 at 12:51 +0000 (UTC)
[...] de 2006 a las 13:51 y está archivada en: Enlaces. Puedes dejar un comentario, o enviar un trackback desde tusitio. [...]
August 13th, 2006 at 15:28 +0000 (UTC)
[...] alexander kirk - JavaScript Tricks And Good Programming Style [...]
August 13th, 2006 at 20:45 +0000 (UTC)
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.
August 17th, 2006 at 01:42 +0000 (UTC)
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.
August 17th, 2006 at 01:49 +0000 (UTC)
... and now I've read the rest of Michael's post, saw
window.undefined = window.undefined;
... and retract everything I just said. Sorry everyone.
August 17th, 2006 at 08:42 +0000 (UTC)
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....
August 19th, 2006 at 20:40 +0000 (UTC)
[...] Optional parameter and default valueParameters HintsSearch JavaScript documentationThe self variableReduce indentation amount | read here in detail… [...]
August 27th, 2006 at 16:10 +0000 (UTC)
[...] 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: [...]
September 1st, 2006 at 06:00 +0000 (UTC)
[...] alexander kirk » Blog Archive » JavaScript Tricks And Good Programming Style (tags: javascript) [...]
October 5th, 2006 at 02:17 +0000 (UTC)
[...] 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) [...]