Hacker News new | past | comments | ask | show | jobs | submit login
Essential JavaScript Design Patterns For Beginners (addyosmani.com)
336 points by mfalcon on Feb 17, 2012 | hide | past | favorite | 35 comments



This is a good, if long-winded document. At over 100 pages, it will take a dedicated reader to sift the gold.

As other patterns covered display aspects of DRY-ness with JavaScript, let's take a look at how to write DRY code using jQuery. Note that where jQuery is used, you can easily substitute selections using vanilla JavaScript because jQuery is just JavaScript at an abstracted level.

That is jibberish, and it shows in the code examples: Using an ES5 shim is best-practice these days, so that you can use Array.prototype.forEach and not jQuery.each (with its weird argument order), amongst other things that are commonplace in modern JavaScript code. Yet the auther defers to jQuery throughout.

As jQuery is accepted as one of the best options for DOM-manipulation and selection, we'll be using it for our DOM-related examples.

More likely that the document is filled with jQuery because the author is heavily involved with the jQuery project, no?


> Using an ES5 shim is best-practice these days

Says who?

I'm guessing about 90% of the reason most people start using a library like jQuery is because it lets you find almost any DOM element in a single step with a familiar syntax. And about 9% of the rest is probably to make AJAX less painful. Does your unspecified ES5 shim provide these features?

If not, a lot of people are going to be using a library like jQuery anyway, and you're advocating using an extra (unspecified) library to get portable utility functions that also work in older browsers. What happened to DRY? ;-)

This isn't to say that polyfilling doesn't have its place or isn't useful. I just challenge your claim about what is best practice, because I think the argument is far from compelling in the common case that a developer is already using another library with similar utility functions (which many, many general purpose JS libraries have, because the language itself was so slow to provide them; it's like strings in C++ all over again).


You don't really need shims for selectors these days, querySelector() and querySelectorAll() are supported by all modern browsers and IE8.


The APIs might exist, but whether they are superior to the established functionality of a library like jQuery is debatable.

For one thing, these APIs only support selectors as expressive as the browser they're running in, which could be a significant disadvantage for IE8 in particular.

For another thing, querySelectorAll() doesn't actually return the array one might expect, but rather a NodeList. That means that doing obvious things like iterating over all the elements you find with forEach() is, well, not so obvious at all.

So I think I stand by my previous position: a lot of people are going to have good reasons to use another library anyway, in which case they might also have access to various convenience functions, in which case relying on ES5 for the same functionality and therefore relying on an ES5 shim to support older browsers is unnecessary.


Why would you include ES5 shims instead of using code that's well tested, already written, and bundled in a neat package.

jQuery is the de-facto DOM library (whether it should be or not), failure to see this is ignorance.


Why would you include ES5 shims instead of using code that's well tested, already written, and bundled in a neat package.

I'm not sure how much you really need to test an each() loop. It is a very simple thing as are most of the other features introduced in ES5 that can be shimmed. jQuery doesn't include many of these new features, and I disagree with you that you should just omit them from your code because your DOM library, of all things, doesn't include them.

All the ES5 shims do is implement these functions exactly to the standard, and doing so is a very simple thing. Further, the code in a shim is only run in old versions of IE: New IE, and pretty much all running versions of non-IE browsers, implement these functions in native code (which obviously is much faster). jQuery does not defer to Array.prototype.forEach if it is available.

jQuery is the de-facto DOM library

No, it really isn't. There's a huge distance to travel from being the most popular to being "de-facto."


>> Using an ES5 shim is best-practice these days

Hmmm, seems like a huge distance to travel from being 'I think ES5 shims are awesome' to a 'best-practice' these days. I'd be hesitant to use the kriskowal library in production, especially since it states 'This package requires quite a bit more attention and testing. It is not likely to behave as advertised in a large cross-section of browsers.'


Well I'd love to know which DOM library you're hiding behind your words, that you believe is de facto.


There is no de facto DOM library. I just said as much ... jQuery would be "de facto" if projects adopted it without a second thought, and in my experience this doesn't happen except with beginners.


These 'beginners' are building working sites left and right every day. They don't need to feel they are on the bleeding edge of html, they just want their stuff to work reliably with a reasonable cost. Most of them don't write blogs, don't have githubs, don't own node packages. They develop web sites the same way that you and I drive our cars. They adopt jQuery without a second thought, and why shouldn't they?


jQuery's extreme ubiquity arguably makes it the de facto DOM abstraction library, and this does conform to the definition.

Its demographic, or target audience, is irrelevant, because it is everywhere, and much to the detriment of 'pure javascript' (some might argue).

It'll remain that way until differing browser implementations converge and obviate the need for an abstraction that papers over the cracks.


That is jibberish, and it shows in the code examples: Using an ES5 shim is best-practice these days, so that you can use Array.prototype.forEach and not jQuery.each (with its weird argument order), amongst other things that are commonplace in modern JavaScript code. Yet the auther defers to jQuery throughout.

Could you expand on this a little bit, particularly the "other things that are commonplace in modern JavaScript code"? As someone who's just getting into heavy client-side JS, it still feels like the whole world is very jQuery-centric. In particular, material targeted at beginners (such as this article) tends to lean heavily on jQuery for anything more complex than really basic core language stuff. I'd be very interested in alternatives, if only to broaden my horizons.


"Modern JavaScript code", like I was saying, uses an ES5 shim and functions like Array's forEach, map, some, every, Functions' bind, etc. I just didn't want it to sound like I thought the only thing ES5 provides is forEach.

Regarding your other question, jQuery/JavaScript is a false dichotomy. jQuery's just another DOM library with a few utility functions.


He is trying to explain patters and how to use it in real world solutions; that means cross-browsers solutions that do not rely on ES5.


jQueryIsAwesome, you must not know what an ES5 shim is. It is a cross-browser solution.

https://github.com/kriskowal/es5-shim

Especially when you note that JavaScript is not just run in browsers these days, and that most server-side deployments do implement ES5 features, it makes very little sense for modern tutorials to avoid ES5 features unless they are trying to demonstrate drop-in snippets with no library dependencies. It seems clear that the author is not shy about including other libraries with his code.


From the readme for that library:

> This package requires quite a bit more attention and testing.

> It is not likely to behave as advertised in a large cross-section of browsers.

That doesn't sound like a cross-browser solution. In fact, it doesn't sound like a professional-level solution at all.

Perhaps one day it will be, but if you're arguing that using this kind of library is a best practice today and that developers should eschew jQuery in favour of such alternatives, then I don't think your position is credible at all.


> In many cases, this means that these shims cause many ES5 methods to silently fail.

Considering you don't even get the opportunity to detect the problem and turn off the failing progressive enhancement in favor of the non-js behavior, this doesn't seem like a realistic alternative to just writing pre-ES5 code that's known to work.


That's just FUD though. The potentially "silently failing" methods are all documented, and do not include any of the most commonly used ES5 features (Function.prototype.bind, Array.prototype.filter, etc.).


The jQuery-centric feeling is just because it is the most common library right now. The part about leaning heavily on a framework for anything that is not basic id more due to the fact that writing cross browser DOM and evdent manipulation is really a PITA.

If you are interested in alternatives to jQuery there are many of them available. I am personaly a big fan of the Dojo toolkit, since it is kind of batteries-included, it does less magic $ stuff and it supports a decent module system.


This is a good, if long-winded document. At over 100 pages, it will take a dedicated reader to sift the gold.

I defer to him who made a plea, for consultants' reports to become poetry: https://www.youtube.com/watch?v=GUSLEJB9_LU

Perhaps, as _why's Poignant Guide, the poet goes too far: and then we lose too much substance. But 150 pages? Au revoir: there must be a cleaner, deeper essence. For I delight in the everyday code, which is fun, dramatic, sweeping, and wry, and manufactured examples of jQuery's road are nowhere as cool as an eval/apply.

The worst part is, he knows not what he does. For I opened him up to a random page, and saw what he said -- and I don't mean to fuss, but I can't help but feeling a little rage:

The Iterator Pattern is a design pattern where iterators (objects that allow us to traverse through all the elements of a collection) access the elements of an aggregate object sequentially without needing to expose its underlying form.

Iterators encapsulate the internal structure of how that particular iteration occurs - in the case of jQuery's $(el).each() iterator, you are actually able to use the underlying code behind $.each() to iterate through a collection, without needing to see or understand the code working behind the scenes that's providing this capability. This is a pattern similar to the facade, except it deals explicitly with iteration.

No, my good sir, ten times no. The iterator pattern as was discussed (in Design Patterns, and such and so), has nothing to do with that construct. Iterators speak polymorphism out loud, freeing us from finities and the rest of the crowd. Let me illustrate with a simple collection of rational numbers -- in Cantor's bijection:

    function gcd(lo, hi) {
        return lo > hi ? gcd(hi, lo) : lo === 0 ? hi : gcd(hi % lo, lo);
    }
    function rational_iterator() {
        var num = 0, denom = 1;
        return {
            hasNext: function () {
                return true;
            },
            next: function recurse() {
                var rat = [num, denom];
                if (denom === 1) {
                    denom = num + 1;
                    num = 1;
                } else {
                    denom -= 1;
                    num += 1;
                }
                return gcd(rat[0], rat[1]) === 1 ? rat : recurse();
            }
        };
    }
And perhaps arrays are not quite your style, so you might like me to convert them to strings. That requires a new map method -- I'm not in denial -- but that's pretty simple in the grand scheme of things:

    function map(seq, fn) {
        var i = 0;
        return {
            hasNext: function () {
                return seq.hasNext();
            },
            next: function () {
                return fn(seq.next(), i++);
            }
        };
    }
    var rationals_as_strings = map(rational_iterator(), function (x) { return x.join("/"); });
Our lazy maps and infinite sequence save us from any pro-jQuery pretense and instead show us a taste of that vision of knowing your code, with modest concision.


> No, my good sir, ten times no. The iterator pattern as was discussed (in Design Patterns, and such and so), has nothing to do with that construct. Iterators speak polymorphism out loud, freeing us from finities and the rest of the crowd.

That is not entirely true, internal iterators in the style of Smalltalk and Ruby have all the capabilities of GOF-style external iterators, and jQuery (or underscore's) each is a restricted kind of internal iterator.

And while that is (sadly) not supported at the moment these could very well delegate to an arbitrary implementor of Javascript's own internal iterator protocols (JS 1.6's "Array extras"), leading exactly to the capabilities you describe (including but not limited to polymorphism).

Furthermore, external iterators are of little use, value and class when you have blocks, or at least "full" anonymous functions. It's unsurprising to have them in the C++ and Java-based GOF, but they don't belong anywhere near JavaScript.


Brilliant.


One book that I found to be really good was 'JavaScript Patterns' by Stoyan Stefanov. Great book, though I am not sure how much this article overlaps with the book.

[http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp...]


Definitely recommend Stefanov's JavaScript Patterns. I'd suggest reading both concurrently since JavaScript Patterns covers best practices with JS as a whole while Essential JS is focused almost exclusively on design patterns.


On just length alone, if this is the beginner's version, would love to see the intermediate/advanced versions ;)

I like the references to Backbone.js and other modern frameworks...the explanation of Backbone's router vs Spine's controller was particularly helpful, mostly because I know Backbone's router is not a controller but hadn't taken the time to see how Spine does it.

I thought the introduction...everything before the actual examples/cases, was a little too long-winded for me. It was interesting to read as an experienced programmer, but I can't imagine a beginner trudging through all of that without seeing a simplified use case to break up the long narrative text. If the audience for this book is beginners, why go into such great detail about the philosophy of patterns (including a discussion of antipatterns) when most beginners are at the level where they probably don't know much OOP or even things like closures?

Otherwise, another fantastic addition to the open-source bookshelf.


There is a better way to do the mixins in JS. See this awesome post by Angus Croll:

http://javascriptweblog.wordpress.com/2011/05/31/a-fresh-loo...

`this` in JS generally incurs a lot of wrath, but this is one cool hack with `this`.


Dunno, seems to be a regurgitation of GoF patterns along with some JS-specific ones. I'd rather see a link to the GoF patterns, with a concentration on the JS-specific ones. Shouldn't articles be DRY as well?


In fact, I would rather see someone forgetting about GoF, focusing on the problems that do exist in this different environment (Javascript) and coming up with solutions to those, thus formulating patterns that are really relevant in the context of Javascript.

Because, you know, the pattern is not the solution, but the sum of problem + context + solution.


I think you can shave off the "for beginners" from the title. It's really worth the read no matter your skill level. There are so many interesting and different ways to use JavaScript that I think almost everyone can gain something from this. Really enjoying it so far :)


The revealing module pattern is one of my favorites for writing jQuery plugins. This is a great reference for front-end developers once they've gotten good enough to be dangerous with JavaScript. I stumbled across this a while ago after writing my first non-trivial mobile web app from scratch with JavaScript, and it made everything click.


I got the same 'click' recently, from reading about js design patterns (the oreilly book in particular), while working on a non-trivial html5 app. Ive been conscious of my tendency to write spaghetti code for a while, but didnt have any concrete guidelines on how to level up (just general advice like 'read good code' and 'fork github projects').

I had the sudden realization that using and recognizing design patterns was exactly what I was missing to get over the beginner-intermediate hump in the learning curve. I'd heard of design patterns before, but they'd come across as a rather lofty concept.

After a second and third look, I realized its immediately practical boilerplate stuff that one can copy/paste (and even more importantly, recognize in 3rd-party plugins and frameworks). Design patterns should follow after reading a 'cookbook'. Its just a name for techniques beyond your basic for/while loops (which could be thought of as the simplest of design patterns).

Pub/Sub can be viewed as a simple form of the observer pattern, and MVC is a more elaborate form (notice that in the article's TOC, MVC comes right after Observer). I've lost a lot of time banging my head on MVC too soon. For some reason, the introductory articles on MVC I was finding made scant mention of design patterns. Great to see more material discussing the concepts together lately.


To be specific, jQuery is a utility library with a large set of DOM utilities. Sizzle is actually the DOM workhorse.

As for the article: it's a valuable resource. Especially for those that believe jQuery is JavaScript. It's a goodie.


Wow, this will be hard for a beginner to process. Unless that beginner has a week to read it, and has a good background in programming languages and design patterns. And then he probably won't need to read this.


Freaking amazing, I've been utilizing MVC heavily to develop HTML5 games. Mainly borrowing ideas from Android and iPhone development with their layout engines and staying out of the way as much as possible.


Wow and I thought I knew Javascript




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: