Digital Web Magazine

The web professional's online magazine of choice.

Seven JavaScript Techniques You Should Be Using Today : Comments

By Dustin Diaz

April 23, 2007

Comments

Tobie Langel

April 24, 2007 1:38 AM

Why not simply:


var FLAGS = { w3: !!(document.getElementById && document.createElement), ie: !!window.ActiveX,
};

Jon Cram

April 24, 2007 2:01 AM

That’s a great set of techniques!

I have a few questions, not necessarily about the techniques, but about a few JS features that I don’t yet fully understand and which are covered here.

Firstly, why:

var w3 = !!(document.getElementById && document.createElement);

and not:

var w3 = (document.getElementById && document.createElement);

I don’t see how the double boolean ‘not’ helps. Doesn’t a double not have the same affect as no not at all?

Secondly, and taking the asyncRequest function as an example, I notice that the variable asyncRequest is defined as a function that contains some functions and variables – how does this relate to OOP? Is this the JS way of creating classes?

I’ve oft wondered how I can create class-like structures in JS such that I can make calls such as:

object.myFunction()

instead of:

myFunction(object);

Following on from this, taking the asyncRequest function as an example again, how would you call asyncRequest and how do you reference the functions within it?

If I wanted to use asyncRequest to create an AJAX-handling object, would I do something like:

var myVariable = asyncRequest();

I can see how that would be the way to go about it if asyncRequest returned an XMLHttpRequest object, but as far as I can tell asyncRequest returns a function which itself returns an XMLHttpRequest object (or MS variation thereof).

I’m a bit hazy on these areas of JS and wondered if you could clarify things a little.

Carey

April 24, 2007 2:36 AM

Jon: (document.getElementById && document.createElement) is document.createElement, not true. I don’t know how much damage you can do keeping old references around, though; maybe you can cause memory leaks in old versions of IE. I recommend installing Firebug and using it to test simple expressions.

Note how the anonymous function used to define asyncRequest is called immediately with the () at the end. It would be more explicit to define a function makeAsyncRequest() {...} then say var asyncRequest = makeAsyncRequest(), but this would leave the function around after running it.

Personally, I would say var asyncRequest then assign it in one of the tests, for more obvious code. I’d also use Tobie’s code for the map.

Jon Cram

April 24, 2007 3:32 AM

Carey: Thanks for your comments – that certainly helps and I’ll take a peek at Firebug and see how that can help.

In general, is:

var w3 = !!(document.getElementById && document.createElement);

the same as:

var w3 = (document.getElementById && document.createElement) ? true : false;

The same, that is, with respect to the value assigned to ‘w3’. I’m assuming it is as I can’t see why it isn’t.

Given this, I’d be inclined to opt for the latter as, for me, it’s more clear what value is being assigned to ‘w3’ and why.

Matthew Pennell

April 24, 2007 5:09 AM

That’s correct, Jon, although I’d imagine that there is a tiny processing overhead in performing the if…else operation instead of a simple type conversion.

I’m sure Dustin himself will be along to field these questions as soon as he’s awake. :)

Rob Cherny

April 24, 2007 6:23 AM

Great tips. Love the scope comments, I’m a big fan.

Event delegation is especially useful if you’re scripting complex things which are being added and removed to the page. When you modify the DOM, some event handlers can vanish. Event delegation can help solve this problem. I did a blurb on it a while back.

Ben Long

April 24, 2007 8:38 AM

Thanks again – your insights are always inspiring. I was not surprised (maybe a little) when I reached the bottom of the article and saw your face. Few people could of written this article.

You hear stories about a Google employee exodus. It appears they’re still attracting the best.

It would be great if you could take your recommendation on encapsulating entire javascript applications inside of a closure to the next level. Possibly providing working examples and best practice patterns!

Cheers.

David Stamm

April 24, 2007 9:11 AM

Thanks for the great article. I’m a little unclear about the advantages gained by encapsulating functionality inside a closure. Is this akin to the “data hiding” concept we all were taught back in object-oriented kindergarten?

Mike

April 24, 2007 9:24 AM

another great one from digital-web

queued @ tweako

Dustin Diaz

April 24, 2007 9:25 AM

Hello all, I’m awake now. Nice to see the article is up. In regards to the closures that you might see in odd spots, this is essentially regular practice for me. Anytime I have bare logic sitting around, I move it into a function for safeguarding purposes. That even goes for the flags, and the asyncRequest examples.

Re: asyncRequest, that’s not a function that returns an instance but rather I’ve encapsulated the logic to get the XMLHttpRequest object, and use asyncRequest as a function that allows you to make the calls to the server. It is essentially like using YAHOO.util.Connect.asyncRequest. The Connect object is a singleton in that case.

Re: Event Delegation; I would encourage some folks to check out Christian’s article if you haven’t poked around with that technique yet. It can really speed up your applications when speed is critical. This technique is used all throughout the new Yahoo Mail beta.

@Jon: Yes, those two are essentially the same. Getting into a preference of style is usually where I take a step back and allow others to do what suites them best. Nobody is right or wrong here ;)

Chris

April 24, 2007 10:15 AM

I try to use as less closures as possible, as they are know to have big memory leaks.

Dustin Diaz

April 24, 2007 10:17 AM

Chris: That’s the biggest misconception of closures. None of the closures I’ve demonstrated in this article create a memory leak.

Peter Michaux

April 24, 2007 10:46 AM

Dustin, great article! I really like #7, especially in the browser scripting environment.

Marty Stake

April 24, 2007 10:47 AM

Awesome article Dustin. Resources like this are so valuable in tying together how modern Javascript is written. If there is one glaring missing piece of JS knowledge out there, its best practices for how to write this stuff. Thanks for your insight.

Chinmay

April 24, 2007 12:09 PM

As regards the Selector example you gave, it might be much easier to simply use the generic Array methods in the new Firefox versions (and add them for other browsers)

e,g,

var ids = Array.map(document.getElementsByTagName(*), function (e) { return e.id;});

I believe this method (map/reduce) is more flexible than adding arbitrary callbacks

Tino Zijdel

April 24, 2007 2:17 PM

Although the branching-technique is quite useful I think the examples are somewhat flawed.

As for the asyncRequest() example, what will happen when I want to do this:

asyncRequest(‘GET’, ‘foo.php’, function(obj) { alert(obj.responseText); });
asyncRequest(‘GET’, ‘bar.php’, function(obj) { alert(obj.responseText); });

?
And could someone please explain why this example is using an interval instead of the onreadystatechange event handler? Is there some compelling reason to do so?

As for the addListener() example, first of all be aware that addEventListener is not formally defined for the window object. Secondly try this in IE and in some other modern browser:

function clickHandler() { alert(‘you clicked!’); }
addListener(document, ‘click’, clickHandler);
addListener(document, ‘click’, clickHandler);

Funny how IE alerts twice right?

now let’s try to create a removeListener function (I’ll leave that as an excercise) and add the following line:

removeListener(document, ‘click’, clickHandler);

Funny how IE is still alerting twice right, while other browsers don’t alert at all anymore?

Dustin Diaz

April 24, 2007 2:41 PM

@Chinnay: Callbacks are not arbitrary. The example is actually targeted for custom element collectors, and not the native implementation of getElementsByTagName. Regardless, the collector stills returns a collection that you can use later (caching). You can then use a forEach (or map for your case) to batch more functionality on it a later time.

@Tino: That’s actually a good catch. It’s flawed in the sense that you’d need to watch for two requests happening at the same time. To avoid that, we’d probably want to branch the object creation method which will give us new instances on each request :)

In regards to the addListener function, I don’t see why having something alert twice is a problem, especially when you’ve assigned two listeners to it… however the removeListener function would require a little more inner-working trickery since we’ve created a wrapped function. You can dig into YUI’s addListener function and see some of the logic they’ve done to work around this.

Great comments everyone! This is all very positive feedback.

Tino Zijdel

April 24, 2007 3:31 PM

Dustin: I actually use queueing in my own little Ajax framework for async requests.

Also I have seen async requests failing in Opera when reusing the XHR-instance (even when the previous request was completed). It seems to be fixed in 9.2 though so I will need to check which versions were affected. Or maybe that is why you are using the interval iso onreadystatechange?

As for addListener: the problem is that it is different behavior from the W3C event model (and thus in this example different behavior in different browsers). The DOM event specification states that “If multiple identical EventListeners are registered on the same EventTarget with the same parameters the duplicate instances are discarded.” so IE’s eventmodel is also lacking in this area.

I did however use the branching technique to improve the addEvent solution Dean and I came up with ;)

And sorry, I don’t really like YUI – it’s way too bloated and complicated

Ian Flett / Yorrix

April 24, 2007 4:04 PM

Hi Dustin,

That was a great article; I really enjoyed and appreciated it. It was refreshing to read an article with regard to best practices; I think I have only read one other thus far, and the other was more concerned with code maintenance than best practice techniques to get the most from your code.

I found it fairly easy to follow, coupled with what I have already read of the O

Dustin Diaz

April 24, 2007 4:08 PM

@Tino: You’re definitely entitled to a preference in libraries, but YUI is one of the least bloated JavaScript libraries out there. It sounds like someone just never dug into the docs ;)

Thanks for digging up that info on addEventListener. I would have never known about the duplication issue that it’s supposed to ignore them. I guess I’ve never ran into it because I have never added two duplicate listeners to the same object. If that’s the case, that’s an issue with IE that many addEvent functions don’t take into account.

Dustin Diaz

April 24, 2007 4:31 PM

Wow, what a response Ian. I must have been posting while you were writing your story :)

I am flattered that you found it so useful. I guess unemployment got the best of me so I decided to do something practical and write something up.

re: closures; they are very powerful in many different aspects. However don’t get into the habit of using them just for the sake of doing it. There are good places, and bad places, and even some that cause memory leaks (as someone pointed out (but in a completely different context)).

Reinventing the wheel (or as I like to say “innovate the wheel”) is a philosophy that many developers shudder at. In one sense, it may be a bad business practice, but I rarely see the case when it’s a bad engineering practice. You never know if your implementation is going to be better (eg: performance, maintenance, elegance, better API, etc). Plus, sometimes it’s best just to recreate something purely for educational purposes. You can gain invaluable experience this way.

Tino Zijdel

April 24, 2007 5:06 PM

Dustin: yui_2.2.2.zip is a whopping 8MB, and even events.js is still 75KB and going through that doesn’t really give me much insight in how YUI is actually solving the problem – it will take me some time to figure it out. At first glance it does a lot of things that I don’t regularly need. Note that I even find prototype or Dean Edwards’ Base too bloated for my needs, although they do match up to what I expect in a library ;)

Without people “reinventing the wheel” we will never have improvements over our current techniques, and I’m just a sucker to reinvent the wheel over and over again :P

Stephen Clay

April 24, 2007 7:35 PM

Anyone not getting “branching” should check out Oliver Steele’s great article on Memoization in Javascript. Essentially you can overwrite a function that does work with one that just has to return the result, and you can rewrite the function within itself!

Ross Harmes

April 24, 2007 10:17 PM

Tino, YUI 2.2.2 is not 8MB. That zip file includes much more than just the JS files (documentation, examples and three versions of each lib). A good look at how big each YUI utility actually is can be found here: Weighing in on Pageweight.

Jon Cram

April 25, 2007 1:08 AM

Dustin: “unemployment got the best of me so I decided to do something practical and write something up” – I’d wholeheartedly recommend taking things one step further and get a whopping 500 page book written up on this topic!

Michal Kuklis

April 25, 2007 1:22 PM

cool stuff.
Thanks

matt

April 25, 2007 1:40 PM

Not sure about the !! trick. Sure it’s nifty, but it’s often well worth the effort to streer clear of such “nifty tricks” – who ever else is reading the code is sure gonna get confused. Unless of course your enlighten them, and that isn’t always possible.

Ian Flett / Yorrix

April 25, 2007 3:40 PM

@Dustin: No worries; and I know I tend to have a tendency to use an nth power of words when only n would do. Looks around innocently.

I understand that closures should not be over used, but I figure excessive in R&D and moderate in practice should work. If I think a closure might help, try it; if it doesn

Adam

April 25, 2007 5:12 PM

Regarding your flags:


var FLAGS = { w3: function() { return !!(document.getElementById && document.createElement); }(), ie: function() { return !!window.ActiveX; }()
};

A couple things I’d like to ask:

1) Since JS has truthy/falsy values is the !! required at all? I don’t see how you would need to cast it to a boolean in any case – if you’re &&‘ing two values then the result’s always going to be a boolean value.
2) Why did you chose to use function()‘s to get the value, rather than set the variable once? Surely this would save cycles if the function’s called multiple times.

Jon Cram

April 26, 2007 4:43 AM

@Ian: It’s good that you bring up the topic of comments and documentation.

It only takes a small amount of experience of working with code written by others to really appreciate the benefits of comments.

Well commented code significantly reduces the time it takes for another developer to understand what is going on and from this perspective a consistent commenting process should be encouraged by all companies employing more than one developer – it simply hammers down the ‘What does this code do?’ portion of maintenance, cutting down maintenance time and increasing productivity over a given period. Developers are happier as the work flows more evenly and companies, quite simply, save money.

UML documentation is spot-on for modelling and describing object-oriented systems, but there’s no standard I’m aware of for documenting procedural code, much of which occurs in JS and much of which occurs inside OOP methods.

For purely procedural processes i.e. the internal goings on of JS functions and OOP methods, I find the following design process to be very good. This assumes you’ve already determined what a function needs to achieve.

1) Produce a flowchart for the logic processes.

Do this for every single function. It doesn’t have to be fancy Visio, a simple pen and paper rough sketch will do, which can be computerised later if you need to share it with other developers (which, eventually, you will).

Go through the flowchart with some sets of test data, determining the values of various variables and the result of various operations as they occur. Doing this highlights logic errors that might not otherwise be apparent. Testing doesn’t have to be exhaustive, just go through a set of inputs for which you can be sure of the output. Repeat until the flowchart correctly models your function logic.

2) From the flowchart, produce a set of numbered steps the function goes through.

e.g.
1. Validate user name 1.1 More than 8 and less than 12 characters long? 1.2 Contains no spaces? 1.3 Doesn’t start with a number?

2. Validate date of birth 2.1 …

3) Pop your list of numbered steps at the start of your function/method. Another developer can scan through this and tell what your function does without having to get near the code.

4) Pop your list of steps in again, following each step with the required code.

This let’s you easily keep track of how complete your function is and is great for debugging and understanding your code.

Without comments, you can determine what a line of code does, but that on it’s own is pointless without also knowing what the code should do. What it does compared to what is should do is crucial. This focuses on looking at what code should do not how it does it.

Ultimately, you don’t really care how the code works (assuming the developer has used one of a selection of solutions of equal merit), you just care about what it does and how this compares to what it should do.

And the end result?

A function that:

– uses comments to explain what it should do – uses code to show how it does it and what it actually does

Comments should be written such that a non-developer can scan through them and understand the working of the function without needing to know how the code actually does it.

This makes maintenance a breeze, allows developers to switch roles and code any portion by being able to see what is going on without much effort, and facilitates easy testing by allowing someone to compare what the function should do with what it actually does.

I think every company should have a strict commenting policy such as this.

Anthony

April 27, 2007 3:16 PM

Excellent article Dustin…once again.

One thing I was unclear on was the benefit of using a bridge in your example.

Tino Zijdel

April 29, 2007 5:35 PM

Dustin, FYI: I filed a bugreport concerning YUI’s event-handling. YUI’s model has merits but downsides as well; the fact that is doesn’t adhere to the DOM level 2 event specification being one of them.

As for the ‘weight’ of the library: it is still 8MB I will need to read and sift through since it dictates it’s own approach. I guess I just don’t belong to the audience for these kind of libraries ;)

Brandon Aaron

April 30, 2007 6:51 PM

Great overview of these techniques.

In regards to #5, including a callback in a selector engine has the potential to really hurt performance. However, no one can deny the added power it would provide to filter the results by other criteria. This is why jQuery provides a filter method. You can find the docs for it here: http://docs.jquery.com/DOM/Traversing#filter.28_filter_.29

Thanks for the great article!

mark rushworth

May 10, 2007 8:35 AM

im really interested in the frameworks like scriptaculous and all that as an alternative to flash… even better some let you split scripts into non-intrusive code which is brilliant!

Dustin Diaz

May 16, 2007 3:40 PM

Hi Tino,
If you’re going to file a bug with YUI, you might want to look into filing the same bug against Prototype, Dojo, jQuery, Mochikit, etc. I can guarantee that none of these take that bug into account.

@Brandon, I don’t follow where it actually hurts the performance when adding a callback. You can always omit the callback function, and you should always cache that collection if you don’t intend on adding or removing elements. As I mentioned in the article, the point is to avoid redundant looping.

@Mark: I’m not sure I follow… but it sounds like you’re excited about something cool. I hope you enjoyed the article.

coalala

May 16, 2007 10:35 PM

I understand that closures should not be over used, but I figure excessive in R&D and moderate in practice should work. If I think a closure might help, try it; if it doesn

Sorry, comments are closed.

Media Temple

via Ad Packs