Learning to Love JavaScript
by Glenn Vanderburg
(an excerpt from The 2007 No Fluff, Just Stuff Anthology)
New and stirring things are belittled because if they are not belittled, the humiliating question arises, “Why then are you not taking part in them?”
—H. G. Wells
We Java programmers were dead wrong about JavaScript. We scorned it, recommended against it, and tried our best to avoid working with it (or learning any more about it than we had to, in many cases). Now it’s changing how everyone writes web applications, extending and deepening what a web app can do, and thus changing how people view applications of all kinds.
Recently I’ve started to think more and more about why we got it so wrong. Sure, there were legitimate reasons to beware of JavaScript in the early days—it was slow and buggy, and the browser support was uneven. If most of us had said “It’s not ready for prime time, but just wait,” it would be more understandable. But we dismissed JavaScript much more decisively than that.
I, for one, don’t want to make the same mistake again. In a field as fast-moving as ours, it doesn’t pay to turn up your nose at what is going to be the next major, transformative technology. Why did we misjudge JavaScript so completely? If we understand the answer to that question, we might not make the same mistake again.
In this chapter, I’ll look back at those early days of JavaScript and draw a few conclusions about why JavaScript got its worse-than-deserved reputation. I’ll conclude with an example that highlights some of JavaScript’s deep differences from other mainstream programming languages and that shows how JavaScript’s strength can be found largely in those differences. This is primarily my own personal story of coming full circle with JavaScript, but in spite of that narrow focus, I think it will sound familiar to many and be instructive to others.
Comfortable Confusion
In some ways, JavaScript is Java’s evil twin. They’re the same age: both were announced and first appeared in beta form in 1995 and reached 1.0 in 1996. They’re syntactically similar, and of course the names clearly signal a family relationship.
The first any of us outside Netscape heard about JavaScript was in October of 1995. That’s when Netscape released the first beta of Navigator 2.0. JavaScript wasn’t included in that release, although it was the first release that included support for Java applets. But at the same time, Netscape announced it was working on an in-page scripting language called LiveScript.1
That announcement didn’t get much notice. Java applets were the hot web technology of the day, and it was unclear why Netscape needed two in-browser programming languages or what LiveScript could do that Java couldn’t.
Two months later, LiveScript saw the light of day for the first time, in Navigator 2.0B3. It had been renamed to JavaScript. A lot of eye rolling accompanied that announcement. “Oh, the thing can’t make it on its own merits,” we said. “Why is Sun letting this toy ride Java’s coattails?” I still think that naming decision was a bad idea. It caused no end of confusion. Many nonprogrammers never figured out that Java and JavaScript were two different things.
One consequence of renaming LiveScript was somewhat subtle, and I suspect it had a huge impact on the language’s acceptance. The association with Java made it seem as though JavaScript wasn’t entirely a new thing. It’s true that Java itself still had not reached version 1.0, but for various reasons Java was perceived as far more mature than it really was—for example, another event of December 1995 was that Time magazine named Java one of the top-ten new products of the year. (And they called it a miniaturized programming language. Heh.) There were already books available about Java, with many others (including mine) in the works. So when Netscape unveiled its own new technology called JavaScript, it seemed obvious that the small but already vocal group of Java programmers were the best people to understand and evaluate JavaScript.
And in spite of our skepticism, JavaScript did seem like it might have some uses. For one thing, JavaScript was designed to make it possible to control and communicate between Java applets. So those of us who were interested in Java at the time thought it was worth learning about JavaScript. I remember going to the fourth meeting of JavaMUG (the Dallas/Fort Worth–area Java Users’ Group) in March 1996 to hear Greg Graham, a local developer, talk about JavaScript. Greg is a good speaker, and I enjoyed the talk. But I came away distinctly unimpressed by JavaScript.
It seemed like a toy. It was object-oriented, except in the ways that counted. Everything was public, for one thing. No encapsulation. And then there was the fact that you couldn’t really create subclasses. There was no distinction between instance variables and methods, which seemed like a disaster. You could change a variable to a method on the fly! And that broken, overloaded plus operator. How could they have made a mistake like that when there was Java demonstrating how to do it right?
I wasn’t alone. The particular details of our objections differed from person to person, but for the most part Java programmers were in agreement about JavaScript: we didn’t take it seriously as a programming language. We wanted nothing to do with it.
Collateral Damage
Of course, JavaScript did have real technical problems in the early days. It was buggy and slow, and it worked only in Netscape. Then, when it was supported by Internet Explorer, it wasn’t entirely compatible. Also, it was touted as a way to script applets on a page, but it didn’t take long for Java applets to fail. They were never widely used. So, the usage of JavaScript quickly settled into a sort of lowest common denominator: people used it for what would work reliably and avoided it for other things.
What worked reliably with JavaScript? Flashy, image-based visual
effects, mostly. And people came up with some doozies. Netscape
itself led the way with annoying layers that obscured the useful
parts of the page (the predecessors of today’s “pop-over” ads).
There were flashing rollover effects that made us yearn for the days
when the <blink>
tag was the worst of
the web sins. There were scrolling, flickering banners in the
status bar at the bottom of the browser … and semirandom alert
dialog boxes … and the abuses went on and on. Microsoft began talking
about Dynamic HTML, but it didn’t take long for DHTML to become
nearly synonymous with buggy, slow, unmaintainable websites that worked only with Internet Explorer.
Oh, and don’t forget the security problems. Naturally, when you build a Turing-complete programming language into a document format that gets regularly downloaded to client machines, you have to pay careful attention to security, and Netscape didn’t get it quite right. There were security holes in early versions of JavaScript, and it took a while for that to shake out.
The result? Many technically savvy people turned off the JavaScript support in their browsers. Remember, the percentage of Internet users that could be called “technically savvy” was pretty large in 1996, so that was a big blow. That, in itself, created another reason not to depend on JavaScript: a significant percentage of users wouldn’t be able to use your page.
Those who were using JavaScript weren’t doing it well. The serious programmers had rejected it, so the gap was filled by page and graphic designers, and it showed. Most JavaScript books and tutorials of the era provided horrible examples of JavaScript style, and rather than explaining the language in any kind of systematic way, they mostly provided canned examples. (The one exception I’m aware of was David Flanagan’s JavaScript: The Complete Reference, which continues to be a terrific resource.)
To make matters worse, many people (perhaps most) didn’t learn JavaScript from a book at all. Instead, they would copy snippets of JavaScript from existing web pages, tweaking them to make them work. (For the past two years, I’ve been giving a fairly in-depth, technical talk about JavaScript at NFJS shows, and I always ask the audience a lot of questions about their prior experience with JavaScript. The large majority learned JavaScript by copying code from other web pages, and most will admit that in many cases they never completely understood the JavaScript code they put into production.)
So, most people learned JavaScript by example. That’s a pretty good strategy as long as a couple of conditions hold. The first condition is that people learn from good examples, but that certainly wasn’t the case with JavaScript. The second condition is that the new language should be substantially similar, conceptually, to a language that the student already knows. Many people naturally assumed this was the case with JavaScript because of the name and the syntactic similarity with Java.
In fact, though, JavaScript bears only the most superficial resemblance to Java. It actually comes from a completely different part of the programming language family tree. JavaScript is a direct linear descendant of NewtonScript, Self, Smalltalk … and Lisp. Waldemar Horwat, a seminal figure in the early history of JavaScript, has said that he considers JavaScript to be just another dialect of Common Lisp. There’s a bit of hyperbole in that statement, but if you know both languages, it’s easy to see that there’s a lot of truth in it as well.
So, there’s the situation in a nutshell. A language that was very different from any that had previously seen widespread use, JavaScript was being explored by inexperienced programmers, with other programmers learning by following the examples of those first, inexperienced pioneers. Many web users kept it turned off because of security worries, and most experienced programmers were recommending that it should be avoided.
As if all that weren’t enough, the browser wars soon reached a peak, and JavaScript was a weapon. Both sides evolved it rapidly, sometimes deliberately introducing incompatibilities. So, JavaScript became more capable but at the same time even more troublesome.
No wonder we all hated it.
Reconstruction
All during that time, however, I kept hearing hints that there was more to JavaScript than I had realized. Learning that it was a prototype-based object-oriented language descended from Self really intrigued me. (None of the tutorials I had seen really made that point, presumably because the authors didn’t have the programming language background necessary to make the connection and understand those features of the language.2) Also, people began to point out how many of JavaScript’s problems were due to the browsers they were embedded in, rather than the language itself.
Not that that helped any when you had to get real work done. But then Internet Explorer won, Netscape capitulated, and the browser wars were over. Microsoft shifted its magic bug generator ray3 over toward its CSS implementation (where it has stayed pointed until very recently), and the Mozilla team got serious about compatibility. There were some deep incompatibilities that they couldn’t fix without breaking a lot of existing code written explicitly for Navigator, but for the most part the gap has been narrowed, and the differences between the Internet Explorer and Mozilla/Firefox JavaScript implementations are now quite manageable. And of course, the other browser implementors have tried to follow suit.
JavaScript and its support in browsers began a period of steady improvement. But it’s a mark of how thoroughly we had written JavaScript off that almost nobody noticed this happening. One event that has almost become legend is the introduction of XMLHttpRequest. The most important new DHTML feature, the big missing piece of Ajax, came into Internet Explorer through the side door. The Microsoft Outlook team added the feature as an ActiveX control to enable Outlook Web Access. In their keynotes at the Ajax Experience in 2006, Dion Almaer and Ben Galbraith got a lot of mileage out of the fact that XMLHttpRequest isn’t even mentioned in the press release for the first version of Mozilla that included support for it.
There was a lot of JavaScript activity at this time; it’s just that not much of it was happening in web browsers. Macromedia had built its Flash scripting language, ActionScript, on the foundation provided by JavaScript. Adobe was making most of its applications scriptable and extensible using JavaScript. Apple built JavaScript support into its Sherlock application. And of course, the Mozilla project made the decision that large portions of its browser would actually be written in JavaScript. Clearly, JavaScript wasn’t just a web page language anymore. In fact, during this period JavaScript became the default choice for applications that needed a dynamic, runtime extension and scripting language. This probably happened because nearly every programmer these days knows at least a little JavaScript, plus the fact that there were two high-quality embeddable implementations available (SpiderMonkey, written in C, and Rhino, written in Java).
In about 2000, a few people (notably Brent Ashley, Alex Russell, and Douglas Crockford) began noticing what was really possible with JavaScript. Brent began exploring ways for JavaScript to communicate with the server and work in what we would now recognize as an Ajax style, and in those days when support for XMLHttpRequest was spotty, he devised and/or formalized several other clever ways of doing the trick. He built a website called Remote Scripting Resources and wrote the JavaScript Remote Scripting (JSRS) library to encapsulate the various techniques in an API that was compatible across different browsers.
Alex Russell started the netWindows project, an attempt to build a rich, programmable, graphical environment within web pages, complete with draggable windows and other widgets. netWindows became nWidgets and eventually gave rise to the Dojo project, today one of the premier Ajax toolkits.
Finally, Douglas Crockford began exploring the richness of the language. He built a set of instructional web pages appropriately titled JavaScript: The World’s Most Misunderstood Programming Language, in which he documented many little-known techniques for using JavaScript well and for working around some of its flaws.
Brent, Alex, and Doug (and a few others) were voices crying in the wilderness for a while, but when the rest of the web development community was finally ready to pay attention, their efforts were there making the way easier for us.
What I know of JavaScript during this period, from roughly 2000 to mid-2003, I’ve pieced together after the fact. I certainly wasn’t paying attention at the time. I was asleep in Java land, like so many others. But I was also, while I could find the time, becoming an expert in Ruby, and that experience helped prepare me for my second look at JavaScript.
Then, in June of 2003, my friend David Raphael showed me netWindows. After I picked my jaw up off the floor and David assured me that Alex wasn’t completely insane, I woke up and began looking closely at JavaScript again. And although I didn’t know it at the time, it’s clear now that some folks at Google were doing the same thing.
Revolution
Everybody reading this surely knows the story of the period when Ajax exploded onto the scene. But I’ll cover it briefly for completeness.
The first Ajax application I was aware of was Gmail. It was clear what was going on—that it was downloading a big gob of JavaScript and then doing further server communication behind the scenes, avoiding page refreshes. It was truly impressive, but partly for that reason it seemed a bit out of reach for most project teams. I remember thinking that Google must have built a sophisticated custom tool to make such a complex mix of server- and client-side logic manageable.
The Google application that really opened my eyes was Google Suggest. You might not have seen it (although if you’re using Firefox 2.0 you’ve surely used its latest incarnation, which is baked into the Google search field in the navigation toolbar). As you type your search terms, Google Suggest queries the server for popular searches that match what you’ve typed so far, showing the results to you in a drop-down completion list, ready for you to select.
Google Suggest was more compelling to me than Gmail in two ways. First, it was simple enough in concept that any reasonably good web development team could aspire to include such rich functionality in their applications. Second, it was communicating with the server each time the user pressed a key, which helped me see just how much server interaction was possible in a web app. I was sold.
It didn’t take long after that for Google Maps to hit the street, and at about the same time, Jesse James Garrett coined the term Ajax, sparking an enormous amount of discussion about these techniques by … well, just by making it easier to discuss, because we had a concise, recognizable name for what was previously just a way of using some browser features.
Since then, of course, Ajax has become just the way web applications are built. Along with the explosion in applications, there is now a wide selection of libraries, toolkits, frameworks, and development tools to make JavaScript development easier. I would go so far as to say that’s JavaScript’s new problem: too many choices.
Stop Worrying, and Love the DOM
I’ve mentioned a lot of reasons why JavaScript had such a bad reputation for so long. Some of them are good reasons, and some of them aren’t. But I want to revisit one that I think is crucial. It’s crucial because it’s the one that most clearly illustrates the way programmers sometimes are blinded by their experiences, failing to notice or understand new things that they should be using.
You’ve probably realized what I’m talking about. A big reason we Java programmers failed to understand JavaScript is that it was just too different, in too many ways, from the things we did understand.
Remembering the list of things I didn’t like about JavaScript at first, it’s amazing how many of the items are simply missing constraints. Things are open and dynamic in JavaScript to a degree that they simply aren’t in Java. Java is much more static and restricted. That’s not necessarily a bad thing—at least, it’s not bad in all contexts. But Java programmers understand the language (and their programs written in it) largely in terms of those constraints. We don’t have to worry about what happens when an object changes its class, either when we’re designing a new system or when trying to understand an existing one, because in Java that can’t happen.
So when we encounter a language that has no such restriction, it seems a bit frightening. How do you avoid chaos in such a situation?
I don’t mean to make us sound like a bunch of frightened children. It’s the responsibility of a software developer to avoid chaotic, confusing systems, and we all have a bag of tricks—in the form of concepts, rules, and principles—that we use to fulfill that responsibility. When we’re working in a language that has built-in restrictions, it makes sense to lean on those language features to help. It’s disorienting to move away from that, toward a language that allows much more freedom.
To illustrate, I’m going to tell the story of the first time I ever wrote JavaScript like a native speaker. Up to that point, all of the JavaScript code I had written resembled Java or C, depending on whether I was trying to do things in an object-oriented style. But one day I decided I needed to learn how to play to JavaScript’s strengths. It took a conscious effort to make the change, but I’ve been really glad I did, and I’ve been a happy JavaScript programmer ever since.
I was working on an application that included support for long-range departmental forecasting and planning. The requirements specified a page with a table something like that shown in Figure 1. (Of course, the categories were variable, as was the number of years.)
The detail cells were editable, and the bold cells represented totals across each row or subtotals and grand totals down the columns. Where JavaScript came in was that, of course, the totals were supposed to update dynamically as detail values were filled in or changed.4
I’m embarrassed to admit that I began with the most complicated
case, editing a forecast, because it was the most interesting. I
wanted to try to write idiomatic object-oriented JavaScript, so I
created a new “class” with a name that only a word geek could
love: Totalizator
.5 The table needed to be
marked up in a particular way, but I tried to keep that as simple as
possible. A unique id
attribute identified the table
tag, and
class
attributes were used to mark subtotal
and grand total rows (the final column was assumed to be the total
column).
The only thing the new Totalizator
instance needed to know was the
ID of the table. The object would scan the table for the
appropriate class
attributes, find all the cells that it needed
to deal with in various roles, build a couple of indexes that would
help later with quickly recomputing totals, and install a bunch of
event handlers so that it could know when field values had changed.
My code would have been cleaner if the Prototype library had been
around back then, but it was pretty neatly factored into eleven small
methods. So far, so good. (And so far, I haven’t told you anything
about the idiomatic use of JavaScript. For that matter, at this point
in the story I hadn’t learned much about it either. I have to
confess I was a bit disappointed.)
Then it came time to write the “View Forecast” page. On that
page, naturally, the table was not editable. First I thought “I’ll
just write the same logic on the server side for computing the
totals,” but fortunately that thought lasted only a fraction of a
second. I would use Totalizator
for the read-only case, too.
What must I change to make that work?
It turned out that almost no changes were necessary. All the
cells were still there, in the same roles. There wouldn’t be any
input fields, so the part of the code that installed event handlers
just wouldn’t install any. The only thing that needed rethinking
was getNumberFromCell(cell)
.
That method expected to find an
<input>
element in the table cell,
and in this case there wouldn’t be one … just text. How to
rework that?
I considered several options. The first was to add an
if
statement to the method, but that seemed
clumsy and arbitrary. Then I thought about writing a subclass
(perhaps called ReadOnlyTotalizator
), but I didn’t like that idea for several reasons. Then I briefly
considered applying the Strategy pattern, but that seemed like
killing flies with a bazooka.
You can probably see what I was doing wrong. I was thinking in Java, not JavaScript. I caught myself and began consciously thinking of the ways JavaScript was different, and mostly those ways consisted of being more dynamic, allowing things in times and places where they would be prohibited by Java. One thing I remembered was that individual objects could have their own methods, not associated with any class.
Immediately I saw the easy solution. I changed Totalizator
to
expect the implementation of
getNumberFromCell(cell)
to be passed into
the constructor:
function Totalizator(table_id, gnfc_function) {
this.table_id = table_id;
this.getNumberFromCell = gnfc_function;
// ... code to process table and find cells ...
}
// and then, to create an instance for the read-only case:
new Totalizator('forecast_table', function(cell) {
return Number(cell.innerHTML);
});
You might not agree with me when I call that the easy solution. Easy to implement, perhaps, but easy to use? It seems crazy to make someone supply a missing method every time they create an instance.
But that was just the first draft. There are a couple of ways to
make it better. I supplied useful implementations as properties of
the Totalizator
function itself (not as instance methods). And then
I made one of those the default and made the second constructor
parameter optional by testing it against
undefined
before using it.
Finally, I documented things really well:
function Totalizator(table_id, gnfc_function) {
this.table_id = table_id;
if (gnfc_function) {
this.getNumberFromCell = gnfc_function;
}
// ... code to process table and find cells ...
}
Totalizator.getNumberFromSimpleCell = function(cell) {
return Number(cell.innerHTML);
}
Totalizator.getNumberFromTextInputCell = function(cell) {
var children = cell.children || cell.childNodes;
return Number(children[0].value);
}
Totalizator.prototype.getNumberFromCell = Totalizator.getNumberFromSimpleCell;
With that work done, there’s some flexibility in how you create an instance:
new Totalizator('forecast_table');
new Totalizator('editable_forecast', Totalizator.getNumberFromTextInputCell);
new Totalizator('some_new_forecast_type', function(cell) {
// ... a custom getNumberFromCell method
});
Now let’s reflect on all of that for a bit. Does that design seem sloppy to you? Maybe it does, if you’re still thinking in Java terms. But JavaScript is dynamic, loose, and open for good reasons. If anything, I think my design is wildly overengineered for the situation. But I don’t feel bad about it (except maybe for the class name). It was a great learning exercise, very easy to document, and kept the code nice and DRY. Plus, now that I’ve learned to think in JavaScript’s terms, this solution would occur to me almost instantly and take almost no time to implement. Frankly, looking at the code now, I’m embarrassed by most of it; however, it was a milestone in my understanding of JavaScript.
But the important lesson is this: before I could conceive of that design, I had to really know the language! Just look at all the peculiarities of JavaScript that you have to understand before you could arrive at such a design:
- Functions are first-class objects.
- Methods are just functions attached to objects.
- You can add methods to classes at any time (even after instances have been created).
- Individual objects can have their own methods.
- “Class constructors” are just functions.
- Functions, being objects, can have their own properties.
- You can call functions with fewer (or more) arguments than the function is declared to take.
- If no value is passed for a function argument, it gets the
value
undefined
.
Conclusion
Here is the big mistake I made back in 1996: I dismissed JavaScript without really trying to understand it. I took a quick glance, and it didn’t match what I thought a good language should be, so I turned away. Of course, there isn’t time to do a deep dive on every new language that comes along, but the next time a language appears that offers compelling new capabilities (as JavaScript did) or that smart, experienced, respected developers are recommending, I’m going to make sure I spend some quality time with it, learning to use it as it was intended, before I make up my mind either way.
My friend Stuart Halloway has said that the really important thing about Ajax is that it has tricked us into adopting a more powerful language than we would have chosen on our own. I think that’s true, and I’m glad I’ve learned how to deal responsibly with all the freedom that JavaScript gives me.
But Stu has another version of that quote that I like better: “Ajax is a gateway drug for JavaScript.” I’m glad I’m hooked.
Web Resources
- Time magazine’s ten best products of 1995
- Looking back on this eleven years later, it’s clear that everything Time said about Java was actually true of JavaScript instead.
- Douglas Crockford’s JavaScript pages
- A collection of writings about the world’s most misunderstood programming language.
- Ajaxian.com
- News and commentary about all things Ajax, including cool applications, development tools, and JavaScript tips.
- Firebug
- As of early 2007, the gold standard for JavaScript/Ajax development tools.
-
At the time, many of Netscape’s brand names included the word live. If you’ve ever wondered why Microsoft developed a mania for calling things active—ActiveX, Active Desktop, Active Directory, and so on—it was in reaction to Netscape’s LiveThis, LiveThat, and LiveTheOther. ↩
-
That’s not a harsh criticism; JavaScript has a fairly obscure heritage. It’s like one of a clan of long-lost cousins, if you will. ↩
-
Come on, you know Microsoft must have one. ↩
-
Such behavior is crucial for an app like this. Some quixotic individuals no doubt think that such plans are produced by carefully estimating the fine-grained figures and using the totals that result. Of course, in reality the totals are decided first, and the details are made to fit. Instant feedback about the effect of changes on the totals is very helpful to this process. ↩
-
A totalizator is “a machine for computing and showing totals, especially a pari-mutuel machine showing the total number and amount of bets at a racetrack,” according to American Heritage’s dictionary. There’s definitely some gambling going on when people do five-year forecasts. ↩