Vitaly Friedman - Understanding Advanced JavaScript - 2013
Vitaly Friedman - Understanding Advanced JavaScript - 2013
Imprint
© 2013 Smashing Media GmbH, Freiburg, Germany
ISBN: 978-3-943075-60-1 (Version 1: February 2013)
Cover Design: Ricardo Gimenes
PR & Press: Stephan Poppe
eBook Strategy and Editing: Vitaly Friedman
Technical Editing: Cosima Mielke
Planning and Quality control: Vitaly Friedman, Iris Lješnjanin
Tools: Elja Friedman. Syntax Highlighting: Prism by Lea Verou.
Copywriter: Lucy Leiderman.
Idea & Concept: Smashing Media GmbH
TABLE OF CONTENTS
2
Analyzing Network
Characteristics Using
JavaScript And The DOM
BY PHILIP TELLIS ❧
1. http://www.flickr.com/photos/hertzen/4878410201/in/photostream
2. http://github.com/bluesmoon/boomerang
3
ANALYZING NETWORK CHARACTERISTICS USING JAVASCRIPT AND THE DOM
NETWORK LATENCY
Network latency is typically the time it takes to send a signal across the
network and get a response. It’s also often called roundtrip time or ping
time because it’s the time reported by the PING command. While this is
interesting to network engineers who are diagnosing network prob-
lems, Web developers care more about the time it takes to make an
HTTP request and get a response. Therefore, we’ll define HTTP latency
as the time it takes to make the smallest HTTP request possible, and to
get a response with insignificant server-processing time (i.e. the only
thing the server does is send a response).
Cool tip: Light and electricity travel through fiber8 and copper at
66% the speed of light in a vacuum, or 20 × 100.000.000 kilometres per
second. A good approximation of network latency between points A and
B is four times9 the time it takes light or electricity to travel the dis-
tance. Greg’s Cable Map10 is a good resource to find out the length and
bandwidth of undersea network cables. I’ll leave it to you to put these
pieces together.
NETWORK THROUGHPUT
Network throughput tells us how well a network is being utilized. We
may have a 3-megabit network connection but are effectively using on-
ly 2 megabits because the network has a lot of idle time.
DNS
DNS is a little different from everything else we care about. It works
over UDP and typically happens at a layer that is transparent to
3. http://en.wikipedia.org/wiki/Transmission_Control_Protocol
4. http://en.wikipedia.org/wiki/Internet_Protocol
5. http://en.wikipedia.org/wiki/Internet_protocol_suite
6. http://www.faqs.org/rfcs/rfc2549.html
7. http://en.wikipedia.org/wiki/IP_over_Avian_Carriers
8. http://en.wikipedia.org/wiki/Optical_fiber
9. http://rescomp.stanford.edu/~cheshire/rants/Latency.html
10. http://cablemap.info/
4
JavaScript. We’ll see how best to ascertain the time it takes to do a DNS
lookup.
There is, of course, much more to the network, but determining
these characteristics through JavaScript in the browser gets increasing-
ly harder.
We start a timer, then load a 1 × 1 pixel GIF and measure when its on-
load event fires. The GIF itself is 35 bytes in size and so fits in a single
TCP packet even with HTTP headers added in.
This kinda sorta works, but has inconsistent results. In particular,
the first time you load an image, it will take a little longer than subse-
quent loads—even if we make sure the image isn’t cached. Looking at
5
ANALYZING NETWORK CHARACTERISTICS USING JAVASCRIPT AND THE DOM
the TCP packets that go across the network explains what’s happening,
as we’ll see in the following section.
6
Once the connection is established, it remains open until both ends
decide to close it, by going through a similar handshake.
When we throw HTTP over TCP, we now have an HTTP client (typi-
cally a browser) that initiates the TCP connection and sends the first
data packet (a GET request, for example). If we’re using HTTP/1.1 (which
almost everyone does today), then the default will be to use HTTP keep-
alive ( Connection: keep-alive ). This means that several HTTP re-
quests may take place over the same TCP connection. This is good, be-
cause it means that we reduce the overhead of the handshake (three ex-
tra packets).
Now, unless we have HTTP pipelining11 turned on (and most
browsers and servers turn it off), these requests will happen serially.
We can now modify our code a bit to take the time of the TCP hand-
shake into account, and measure latency accordingly.
11. http://en.wikipedia.org/wiki/HTTP_pipelining
7
ANALYZING NETWORK CHARACTERISTICS USING JAVASCRIPT AND THE DOM
With this code, we can measure both latency and the TCP handshake
time. There is a chance that a TCP connection was already active and
that the first request went through on that connection. In this case, the
two times will be very close to each other. In all other cases, rtt , which
requires two packets, should be approximately 66% of tcp , which re-
quires three packets. Note that I say “approximately,” because network
jitter and different routes at the IP layer can make two packets in the
same TCP connection take different lengths of time to get through.
You’ll notice here that we’ve ignored the fact that the first image
might have also required a DNS lookup. We’ll look at that in part 2.
8
Once this code has completed executing, we should have the network
throughput in kilobits per second stored in bw .
Unfortunately, it isn’t that simple, because of something called TCP
slow-start12.
SLOW-START
In order to avoid network congestion, both ends of a TCP connection
will start sending data slowly and wait for an acknowledgement (an
ACK packet). Remember than an ACK packet means, “I ACKnowledge
what you just sent me.” Every time it receives an ACK without timing
out, it assumes that the other end can operate faster and will send out
more packets before waiting for the next ACK. If an ACK doesn’t come
through in the expected timeframe, it assumes that the other end can-
not operate fast enough and so backs off.
This means that our throughput test above would have been fine as
long as our image is small enough to fit within the current TCP win-
dow, which at the start is set to 2. While this is fine for slow networks, a
fast network really wouldn’t be taxed by so small an image.
Instead, we’ll try by sending across images of increasing size and
measuring the time each takes to download.
12. http://en.wikipedia.org/wiki/Slow-start
9
ANALYZING NETWORK CHARACTERISTICS USING JAVASCRIPT AND THE DOM
For the purpose of the code, the global image object is now an array
with the following structure:
var image = [
{url: …, size: … }
];
An array makes it easy to iterate over the list of images, and we can eas-
ily add large images to the end of the array to test faster network con-
nections.
var i=0;
var ld = function() {
if(i > 0)
image[i-1].end = +new Date;
if(i >= image.length)
done();
else {
var img = new Image;
img.onload = ld;
image[i].start = +new Date;
img.src=image[i].url;
}
i++;
};
Unfortunately, this breaks down when a very slow connection hits one
of the bigger images; so, instead, we add a timeout value for each im-
age, designed so that we hit upon common network connection speeds
quickly. Details of the image sizes and timeout values are listed in this
spreadsheet13.
Our code now looks like this:
var i=0;
var ld = function() {
if(i > 0) {
image[i-1].end = +new Date;
clearTimeout(image[i-1].timer);
}
13. https://spreadsheets.google.com/
ccc?key=0AplxPyCzmQi6dDRBN2JEd190N1hhV1N5cHQtUVdBMUE&hl=en_GB
10
if(i >= image.length ||
(i > 0 && image[i-1].expired))
done();
else {
var img = new Image;
img.onload = ld;
image[i].start = +new Date;
image[i].timer =
setTimeout(function() {
image[i].expired=true
},
image[i].timeout);
img.src=image[i].url;
}
i++;
};
This looks much better—and works much better, too. But we’d see
much variance between multiple runs. The only way to reduce the error
in measurement is to run the test multiple times and take a summary
value, such as the median. It’s a tradeoff between how accurate you
need to be and how long you want the user to wait before the test com-
pletes. Getting network throughput to an order of magnitude is often
as close as you need to be. Knowing whether the user’s connection is
around 64 Kbps or 2 Mbps is useful, but determining whether it’s exact-
ly 2048 or 2500 Kbps is much less useful.
14. http://cablemap.info/
11
ANALYZING NETWORK CHARACTERISTICS USING JAVASCRIPT AND THE DOM
• “Slow-Start17,” Wikipedia
Also, see the links above for congestion control and Nagle’s Algorithm.
• Boomerang19
The Boomerang project on GitHub, where much of this has been imple-
mented. ❧
15. http://en.wikipedia.org/wiki/Transmission_Control_Protocol
16. http://www.amazon.com/TCP-Illustrated-Vol-Addison-Wesley-Professional/dp/
0201633469
17. http://en.wikipedia.org/wiki/Slow-start
18. https://spreadsheets.google.com/
ccc?key=0AplxPyCzmQi6dDRBN2JEd190N1hhV1N5cHQtUVdBMUE&hl=en_GB
19. http://github.com/bluesmoon/boomerang/
12
Introduction To JavaScript
Unit Testing
BY JÖRN ZAEFFERER ❧
You probably know that testing is good, but the first hurdle to over-
come when trying to write unit tests for client-side code is the lack of
any actual units; JavaScript code is written for each page of a website or
each module of an application and is closely intermixed with back-end
logic and related HTML. In the worst case, the code is completely
mixed with HTML, as inline events handlers.
This is likely the case when no JavaScript library for some DOM ab-
straction is being used; writing inline event handlers is much easier
than using the DOM APIs to bind those events. More and more devel-
opers are picking up a library such as jQuery to handle the DOM ab-
straction, allowing them to move those inline events to distinct scripts,
either on the same page or even in a separate JavaScript file. However,
putting the code into separate files doesn’t mean that it is ready to be
tested as a unit.
What is a unit anyway? In the best case, it is a pure function that
you can deal with in some way—a function that always gives you the
same result for a given input. This makes unit testing pretty easy, but
most of the time you need to deal with side effects, which here means
DOM manipulations. It’s still useful to figure out which units we can
structure our code into and to build unit tests accordingly.
13
INTRODUCTION TO JAVASCRIPT UNIT TESTING
That should be enough theory for now. Let’s look at a practical example,
testing some JavaScript code that is currently mixed in with and con-
nected to a page. The code looks for links with title attributes, using
those titles to display when something was posted, as a relative time
value, like “5 days ago”:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8" />
<title>Mangled date examples</title>
<script>
function prettyDate(time){
var date = new Date(time || ""),
diff = ((new Date().getTime() - date.getTime()) /
1000),
day_diff = Math.floor(diff / 86400);
window.onload = function(){
var links = document.getElementsByTagName("a");
for (var i = 0; i < links.length; i++) {
if (links[i].title) {
14
var date = prettyDate(links[i].title);
if (date) {
links[i].innerHTML = date;
}
}
}
};
</script>
</head>
<body>
<ul>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <a href="/2008/01/blah/57/"
title="2008-01-28T20:24:17Z">January 28th, 2008</a>
by <a href="/john/">John Resig</a>
</small>
</li>
<!-- more list items -->
</ul>
</body>
</html>
If you ran that example, you’d see a problem: none of the dates get re-
placed. The code works, though. It loops through all anchors on the
page and checks for a title property on each. If there is one, it passes
it to the prettyDate function. If prettyDate returns a result, it up-
dates the innerHTML of the link with the result.
<!DOCTYPE html>
<html>
<head>
15
INTRODUCTION TO JAVASCRIPT UNIT TESTING
window.onload = function(){
var links = document.getElementsByTagName("a");
for (var i = 0; i < links.length; i++) {
if (links[i].title) {
var date =
prettyDate("2008-01-28T22:25:00Z", links[i].title);
if (date) {
links[i].innerHTML = date;
}
}
}
};
16
</script>
</head>
<body>
<ul>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <a href="/2008/01/blah/57/"
title="2008-01-28T20:24:17Z">January 28th, 2008</a>
by <a href="/john/">John Resig</a>
</small>
</li>
<!-- more list items -->
</ul>
</body>
</html>
Now, the links should say “2 hours ago,” “Yesterday” and so on. That’s
something, but still not an actual testable unit. So, without changing
the code further, all we can do is try to test the resulting DOM changes.
Even if that did work, any small change to the markup would likely
break the test, resulting in a really bad cost-benefit ratio for a test like
that.
Refactoring, Stage 0
Instead, let’s refactor the code just enough to have something that we
can unit test.
We need to make two changes for this to happen: pass the current
date to the prettyDate function as an argument, instead of having it
just use new Date , and extract the function to a separate file so that we
can include the code on a separate page for unit tests.
20. http://provide.smashingmagazine.com/introduction-to-js-unit-testing-code/
1-mangled.html
17
INTRODUCTION TO JAVASCRIPT UNIT TESTING
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8" />
<title>Refactored date examples</title>
<script src="prettydate.js"></script>
<script>
window.onload = function() {
var links = document.getElementsByTagName("a");
for ( var i = 0; i < links.length; i++ ) {
if (links[i].title) {
var date =
prettyDate("2008-01-28T22:25:00Z", links[i].title);
if (date) {
links[i].innerHTML = date;
}
}
}
};
</script>
</head>
<body>
<ul>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <a href="/2008/01/blah/57/"
title="2008-01-28T20:24:17Z">January 28th, 2008</a>
by <a href="/john/">John Resig</a>
</small>
</li>
<!-- more list items -->
</ul>
</body>
</html>
18
function prettyDate(now, time){
var date = new Date(time || ""),
diff = (((new Date(now)).getTime() -
date.getTime()) / 1000),
day_diff = Math.floor(diff / 86400);
Now that we have something to test, let’s write some actual unit tests:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8" />
<title>Refactored date examples</title>
<script src="prettydate.js"></script>
<script>
function test(then, expected) {
21. http://provide.smashingmagazine.com/introduction-to-js-unit-testing-code/2-getting-
somewhere.html
19
INTRODUCTION TO JAVASCRIPT UNIT TESTING
results.total++;
var result = prettyDate("2008-01-28T22:25:00Z",
then);
if (result !== expected) {
results.bad++;
console.log("Expected " + expected + ",
but was " + result);
}
}
var results = {
total: 0,
bad: 0
};
test("2008/01/28 22:24:30", "just now");
test("2008/01/28 22:23:30", "1 minute ago");
test("2008/01/28 21:23:30", "1 hour ago");
test("2008/01/27 22:23:30", "Yesterday");
test("2008/01/26 22:23:30", "2 days ago");
test("2007/01/26 22:23:30", undefined);
console.log("Of " + results.total + " tests, " +
results.bad + " failed, "
+ (results.total - results.bad) + " passed.");
</script>
</head>
<body>
</body>
</html>
This will create an ad-hoc testing framework, using only the console for
output. It has no dependencies to the DOM at all, so you could just as
well run it in a non-browser JavaScript environment, such as Node.js or
Rhino, by extracting the code in the script tag to its own file.
22. http://provide.smashingmagazine.com/introduction-to-js-unit-testing-code/3-first-
test.html
20
If a test fails, it will output the expected and actual result for that
test. In the end, it will output a test summary with the total, failed and
passed number of tests.
If all tests have passed, like they should here, you would see the fol-
lowing in the console:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8" />
<title>Refactored date examples</title>
<script>
test("prettydate basics", function() {
var now = "2008/01/28 22:25:00";
equal(prettyDate(now, "2008/01/28 22:24:30"),
"just now");
23. http://docs.jquery.com/Qunit
21
INTRODUCTION TO JAVASCRIPT UNIT TESTING
Three sections are worth a closer look here. Along with the usual HTML
boilerplate, we have three included files: two files for QUnit
( qunit.css and qunit.js ) and the previous prettydate.js .
Then, there’s another script block with the actual tests. The test
method is called once, passing a string as the first argument (naming
the test) and passing a function as the second argument (which will run
the actual code for this test). This code then defines the now variable,
which gets reused below, then calls the equal method a few times with
varying arguments. The equal method is one of several assertions that
QUnit provides. The first argument is the result of a call to prettyDate ,
with the now variable as the first argument and a date string as the sec-
ond. The second argument to equal is the expected result. If the two ar-
guments to equal are the same value, then the assertion will pass; oth-
erwise, it will fail.
Finally, in the body element is some QUnit-specific markup. These
elements are optional. If present, QUnit will use them to output the test
results.
24. http://provide.smashingmagazine.com/introduction-to-js-unit-testing-code/4-qunit-
test.html
22
With a failed test, the result would look something like this:
Because the test contains a failing assertion, QUnit doesn’t collapse the
results for that test, and we can see immediately what went wrong.
Along with the output of the expected and actual values, we get a diff
between the two, which can be useful for comparing larger strings.
Here, it’s pretty obvious what went wrong.
Refactoring, Stage 1
The assertions are currently somewhat incomplete because we aren’t
yet testing the n weeks ago variant. Before adding it, we should consid-
er refactoring the test code. Currently, we are calling prettyDate for
23
INTRODUCTION TO JAVASCRIPT UNIT TESTING
each assertion and passing the now argument. We could easily refactor
this into a custom assertion method:
Here we’ve extracted the call to prettyDate into the date function, in-
lining the now variable into the function. We end up with just the rele-
vant data for each assertion, making it easier to read, while the underly-
ing abstraction remains pretty obvious.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
25. http://provide.smashingmagazine.com/introduction-to-js-unit-testing-code/5-qunit-test-
refactored.html
24
charset=UTF-8" />
<title>Refactored date examples</title>
<link rel="stylesheet" href="qunit.css" />
<script src="qunit.js"></script>
<script src="prettydate2.js"></script>
<script>
test("prettydate.format", function() {
function date(then, expected) {
equal(prettyDate.format("2008/01/28
22:25:00", then), expected);
}
date("2008/01/28 22:24:30", "just now");
date("2008/01/28 22:23:30", "1 minute ago");
date("2008/01/28 21:23:30", "1 hour ago");
date("2008/01/27 22:23:30", "Yesterday");
date("2008/01/26 22:23:30", "2 days ago");
date("2007/01/26 22:23:30", undefined);
});
test("prettyDate.update", function() {
var links =
document.getElementById("qunit-fixture").getElementsByTagName("a");
equal(links[0].innerHTML, "January 28th, 2008");
equal(links[2].innerHTML, "January 27th, 2008");
prettyDate.update("2008-01-28T22:25:00Z");
equal(links[0].innerHTML, "2 hours ago");
equal(links[2].innerHTML, "Yesterday");
});
25
INTRODUCTION TO JAVASCRIPT UNIT TESTING
<div id="qunit"></div>
<div id="qunit-fixture">
<ul>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <span class="time"><a
href="/2008/01/blah/57/"
title="2008-01-28T20:24:17Z">January 28th,
2008</a></span>
by <span class="author"><a
href="/john/">John Resig</a></span>
</small>
</li>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <span class="time"><a
href="/2008/01/blah/57/"
title="2008-01-27T22:24:17Z">January 27th,
2008</a></span>
by <span class="author"><a
href="/john/">John Resig</a></span>
</small>
</li>
</ul>
</div>
</body>
</html>
var prettyDate = {
format: function(now, time){
var date = new Date(time || ""),
diff = (((new
Date(now)).getTime() - date.getTime()) / 1000),
day_diff = Math.floor(diff /
86400);
26
return;
}
update: function(now) {
var links =
document.getElementsByTagName("a");
for ( var i = 0; i < links.length; i++ )
{
if (links[i].title) {
var date =
prettyDate.format(now, links[i].title);
if (date) {
links[i].innerHTML = date;
}
}
}
}
};
27
INTRODUCTION TO JAVASCRIPT UNIT TESTING
The QUnit-based test for that function starts by selecting all a elements
within the #qunit-fixture element. In the updated markup in the
body element, the <div id="qunit-fixture">…</div> is new. It con-
tains an extract of the markup from our initial example, enough to
write useful tests against. By putting it in the #qunit-fixture ele-
ment, we don’t have to worry about DOM changes from one test affect-
ing other tests, because QUnit will automatically reset the markup after
each test.
Let’s look at the first test for prettyDate.update . After selecting
those anchors, two assertions verify that these have their initial text
values. Afterwards, prettyDate.update is called, passing along a fixed
date (the same as in previous tests). Afterwards, two more assertions
are run, now verifying that the innerHTML property of these elements
have the correctly formatted date, “2 hours ago” and “Yesterday.”
Refactoring, Stage 2
The next test, prettyDate.update, one day later , does nearly the
same thing, except that it passes a different date to pretty-
Date.update and, therefore, expects different results for the two links.
Let’s see if we can refactor these tests to remove the duplication.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8" />
<title>Refactored date examples</title>
<link rel="stylesheet" href="qunit.css" />
<script src="qunit.js"></script>
<script src="prettydate2.js"></script>
<script>
test("prettydate.format", function() {
function date(then, expected) {
equal(prettyDate.format("2008/01/28
22:25:00", then), expected);
}
date("2008/01/28 22:24:30", "just now");
date("2008/01/28 22:23:30", "1 minute ago");
26. http://provide.smashingmagazine.com/introduction-to-js-unit-testing-code/6-qunit-
dom.html
28
date("2008/01/28 21:23:30", "1 hour ago");
date("2008/01/27 22:23:30", "Yesterday");
date("2008/01/26 22:23:30", "2 days ago");
date("2007/01/26 22:23:30", undefined);
});
29
INTRODUCTION TO JAVASCRIPT UNIT TESTING
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8" />
<title>Final date examples</title>
<script src="prettydate2.js"></script>
<script>
window.onload = function() {
prettyDate.update("2008-01-28T22:25:00Z");
};
27. http://provide.smashingmagazine.com/introduction-to-js-unit-testing-code/7-qunit-
dom-refactored.html
30
</script>
</head>
<body>
<ul>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <span class="time"><a href="/2008/
01/blah/57/" title="2008-01-28T20:24:17Z"><span>January
28th, 2008</span></a></span>
by <span class="author"><a
href="/john/">John Resig</a></span>
</small>
</li>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <span class="time"><a href="/2008/
01/blah/57/" title="2008-01-27T22:24:17Z"><span>January
27th, 2008</span></a></span>
by <span class="author"><a
href="/john/">John Resig</a></span>
</small>
</li>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <span class="time"><a href="/2008/
01/blah/57/" title="2008-01-26T22:24:17Z"><span>January
26th, 2008</span></a></span>
by <span class="author"><a
href="/john/">John Resig</a></span>
</small>
</li>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <span class="time"><a href="/2008/
01/blah/57/" title="2008-01-25T22:24:17Z"><span>January
25th, 2008</span></a></span>
31
INTRODUCTION TO JAVASCRIPT UNIT TESTING
by <span class="author"><a
href="/john/">John Resig</a></span>
</small>
</li>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <span class="time"><a href="/2008/
01/blah/57/" title="2008-01-24T22:24:17Z"><span>January
24th, 2008</span></a></span>
by <span class="author"><a
href="/john/">John Resig</a></span>
</small>
</li>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <span class="time"><a href="/2008/
01/blah/57/" title="2008-01-14T22:24:17Z"><span>January
14th, 2008</span></a></span>
by <span class="author"><a
href="/john/">John Resig</a></span>
</small>
</li>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <span class="time"><a href="/2008/
01/blah/57/" title="2008-01-04T22:24:17Z"><span>January
4th, 2008</span></a></span>
by <span class="author"><a
href="/john/">John Resig</a></span>
</small>
</li>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <span class="time"><a href="/2008/
01/blah/57/" title="2007-12-15T22:24:17Z"><span>December
15th, 2008</span></a></span>
32
by <span class="author"><a
href="/john/">John Resig</a></span>
</small>
</li>
</ul>
</body>
</html>
Conclusion
Testing JavaScript code is not just a matter of using some test runner
and writing a few tests; it usually requires some heavy structural
changes when applied to code that has been tested only manually be-
fore. We’ve walked through an example of how to change the code
structure of an existing module to run some tests using an ad-hoc test-
ing framework, then replacing that with a more full-featured frame-
work to get useful visual results.
QUnit itself has a lot more to offer, with specific support for testing
asynchronous code such as timeouts, AJAX and events. Its visual test
runner helps to debug code by making it easy to rerun specific tests and
by providing stack traces for failed assertions and caught exceptions.
For further reading, check out the QUnit Cookbook29. ❧
28. http://provide.smashingmagazine.com/introduction-to-js-unit-testing-code/
8-endstate.html
29. http://qunitjs.com/cookbook
33
JOURNEY THROUGH THE JAVASCRIPT MVC JUNGLE
When writing a Web application from scratch, it’s easy to feel like we
can get by simply by relying on a DOM30 manipulation library (like
jQuery31) and a handful of utility plugins. The problem with this is that
it doesn’t take long to get lost in a nested pile of jQuery callbacks and
DOM elements without any real structure in place for our applications.
In short, we’re stuck with spaghetti code32. Fortunately there are
modern JavaScript frameworks that can assist with bringing structure
and organization to our projects, improving how easily maintainable
they are in the long-run.
30. https://developer.mozilla.org/en/DOM/About_the_Document_Object_Model
31. http://jquery.com
32. http://en.wikipedia.org/wiki/Spaghetti_code
33. http://addyosmani.com/resources/essentialjsdesignpatterns/book/#detailmvcmvp
34
JavaScript ‘MVC’ frameworks that can help us structure our code don’t
always strictly follow the above pattern. Some frameworks will include
the responsibility of the Controller in the View (e.g Backbone.js34)
whilst others add their own opinionated components into the mix as
they feel this is more effective.
For this reason we refer to such frameworks as following the MV*
pattern, that is, you’re likely to have a View and a Model, but more like-
ly to have something else also included.
Note: There also exist variations of MVC known as MVP (Model-
View-Presenter) and MVVM (Model-View ViewModel). If you’re new
to this and feel it’s a lot to take in, don’t worry. It can take a little while
to get your head around patterns, but I’ve written more about the above
patterns in my online book Learning JavaScript Design Patterns35 in
case you need further help.
34. http://backbonejs.org
35. http://addyosmani.com/resources/essentialjsdesignpatterns/book/#detailmvcmvp
35
JOURNEY THROUGH THE JAVASCRIPT MVC JUNGLE
number of HTTP requests required for new Views, you will likely find
yourself inventing many of the pieces that make up an MV* framework
like Backbone or Ember.
At the outset, it isn’t terribly difficult to write an application frame-
work that offers some opinionated way to avoid spaghetti code, howev-
er to say that it is equally as trivial to write something of the standard
of Backbone would be a grossly incorrect assumption.
There’s a lot more that goes into structuring an application than ty-
ing together a DOM manipulation library, templating and routing. Ma-
ture MV* frameworks typically not only include many of the pieces
you would find yourself writing, but also include solutions to problems
you’ll find yourself running into later on down the road. This is a time-
saver that you shouldn’t underestimate the value of.
So, where will you likely need an MV* framework and where won’t
you?
If you’re writing an application that will likely only be communicat-
ing with an API or back-end data service, where much of the heavy lift-
ing for viewing or manipulating that data will be occurring in the
browser, you may find a JavaScript MV* framework useful.
Good examples of applications that fall into this category are
GMail36 and Google Docs37. These applications typically download a
single payload containing all the scripts, stylesheets and markup users
need for common tasks and then perform a lot of additional behavior in
the background. It’s trivial to switch between reading an email or docu-
ment to writing one and you don’t need to ask the application to render
the whole page again at all.
If, however, you’re building an application that still relies on the
server for most of the heavy-lifting of Views/pages and you’re just us-
ing a little JavaScript or jQuery to make things a little more interactive,
an MV framework may be overkill. There certainly are complex Web
applications where the partial rendering of views can* be coupled with
a single-page application effectively, but for everything else, you may
find yourself better sticking to a simpler setup.
36. http://gmail.com
37. http://docs.google.com
36
to using (C++, Java) as well as languages used by Web developers (PHP,
Python, .Net etc). This means that in many cases we are borrowing con-
cepts of how to structure applications from what we have seen done in
the past in these other languages.
In my talk “Digesting JavaScript MVC: Pattern Abuse or Evolu-
tion38”, I brought up the point that there’s currently too much choice
when it comes to what to use for structuring your JavaScript applica-
tion. Part of this problem is fueled by how different JavaScript develop-
ers interpret how a scalable JavaScript application should be orga-
nized—MVC? MVP? MVVM? Something else? This leads to more
frameworks being created with a different take on MV* each week and
ultimately more noise because we’re still trying to establish the “right
way” to do things, if that exists at all. Many developers believe it
doesn’t.
We refer to the current state of new frameworks frequently popping
up as ‘Yet Another Framework Syndrome’ (or YAFS). Whilst innovation
is of course something we should welcome, YAFS can lead to a great
deal of confusion and frustration when developers just want to start
writing an app but don’t want to manually evaluate 30 different options
in order to select something maintainable. In many cases, the differ-
ences between some of these frameworks can be very subtle if not diffi-
cult to distinguish.
38. http://addyosmani.com/blog/digesting-javascript-mvc-pattern-abuse-or-evolution/
39. http://backbonejs.org
40. http://emberjs.com
41. http://angularjs.com
42. http://spinejs.com
43. http://canjs.us
44. http://github.com/wycats
45. http://github.com/jashkenas
37
JOURNEY THROUGH THE JAVASCRIPT MVC JUNGLE
46. http://todomvc.com
47. http://www.todomvc.com
38
Our Suggested Criteria For Selecting A Framework
Selecting a framework is of course about more than simply comparing
the Todo app implementations. This is why, once we’ve filtered down
our selection of potential frameworks to just a few, it’s recommend to
spend some time doing a little due diligence. The framework we opt for
may need to support building non-trivial features and could end up be-
ing used to maintain the app for years to come.
39
JOURNEY THROUGH THE JAVASCRIPT MVC JUNGLE
48. http://dojotoolkit.org
49. http://dylanschiemann.com
40
Q: Didn’t Dojo already solve all of this? Why hasn’t it been the
dominent solution for developers wishing to build more struc-
tured (and more non-trivial) applications?
Years ago, while the JavaScript landscape evolved from adding simple
Ajax and chrome to a page, Dojo was evangelizing a “toolkit” approach
to building complex Web applications.
Many of those features were way ahead of most developers needs.
With the emergence of the browser as the dominant application plat-
form, many of the innovations pioneered in The Dojo Toolkit now ap-
pear in newer toolkits. MVC was just another package that Dojo has
provided for quite some time, along with modular code packages, OO in
JS, UI widgets, cross-browser graphics, templating, internationaliza-
tion, accessibility, data stores, testing frameworks, a build system and
much, much more.
JavaScript libraries shouldn’t end at “query”, which is why Dojo, ear-
ly on, focussed on completing the picture for enterprise grade applica-
tion development. This is the same focus that is has today with MVC,
it’s just another “tool in the arsenal”.
Why is Dojo not the dominant toolkit? Its goal was never to be the
only choice. The goal was to provide an open collection of tools that
could be used with anything else, within projects, and liberally copied
into other work as well. Dojo was criticized for being slow and even af-
ter that was addressed, it was criticized for being slow. Trying to shake
that perception is challenging. It is very hard to document a feature-
rich toolkit. There are 175 sub-packages in Dojo 1.8 and over 1,400 mod-
ules.
That is not only a challenge from a documentation purpose, it also
means that there isn’t one thing that Dojo does. Which is good if you
are building software, but very difficult when you are starting out try-
ing to figure out where to start. These are all things we have been try-
ing to work on for Dojo 1.8, in the form of tutorials and significantly
improved documentation.
In Dojo 1.8, dojox/mvc takes another step towards full maturity. There
has been a lot of investment in time, effort, testing and community
awareness into the package. It focuses on providing an MVC model
that leverages the rest of Dojo. Coupled with dojox/app, an application
framework that is designed to make it easier to build rich applications
41
JOURNEY THROUGH THE JAVASCRIPT MVC JUNGLE
42
Following on from comments previously made by Backbone.js author
Jeremey Ashkenas and Yehuda Katz, TodoMVC now also offers consis-
tent implementations based on an official application specification as
well as routing (or state management).
We don’t pretend that more complex learning applications aren’t
possible (they certainly are), but the simplicity of a Todo app allows de-
velopers to review areas such as code structure, component syntax and
flow, which we feel are enough to enable a comparison between frame-
works and prompt further exploration with a particular solution or set
of solutions.
• Backbone.js50
• Ember.js51
• AngularJS52
• Spine.js53
50. http://documentcloud.github.com/backbone
51. http://emberjs.com
52. http://angularjs.org
53. http://spinejs.com
43
JOURNEY THROUGH THE JAVASCRIPT MVC JUNGLE
• KnockoutJS54 (MVVM)
• Dojo55
• YUI56
• Batman.js57
• Closure58
• Agility.js59
• Knockback.js60
• CanJS65
• Maria.js66
• cujo.js67
• Meteor68
• SocketStream69 + jQuery70
• Ext.js71
54. http://knockoutjs.com
55. http://dojotoolkit.org
56. http://yuilibrary.com
57. http://batmanjs.org
58. http://code.google.com/closure/library/
59. http://agilityjs.com
60. http://kmalakoff.github.com/knockback
61. http://documentcloud.github.com/backbone
62. http://requirejs.org
63. http://emberjs.com
64. http://requirejs.org
65. http://canjs.us
66. https://github.com/petermichaux/maria
67. http://cujojs.github.com
68. http://meteor.com
69. http://www.socketstream.org
70. http://jquery.com
44
• Sammy.js72
• JavaScriptMVC73
• TroopJS75
• Stapes.js76
• soma.js77
• DUEL78
• Fidel79
• Olives80
• PlastronJS81
• Dijon82
• rAppid.js83
• Broke84
• o_O85
• Fun86
71. http://www.sencha.com/products/extjs
72. http://sammyjs.org
73. http://javascriptmvc.com
74. https://developers.google.com/web-toolkit/
75. https://github.com/troopjs
76. http://hay.github.com/stapes
77. http://somajs.github.com/somajs
78. https://bitbucket.org/mckamey/duel/wiki/Home
79. https://github.com/jgallen23/fidel
80. https://github.com/flams/olives
81. https://github.com/rhysbrettbowen/PlastronJS
82. https://github.com/creynders/dijon-framework
83. http://www.rappidjs.com
84. https://github.com/brokenseal/broke
85. http://weepy.github.com/o_O
86. https://github.com/marcuswestin/fun
87. http://angularjs.org
88. http://requirejs.org
89. https://github.com/addyosmani/todomvc/blob/master/vanilla-examples/vanillajs/js/
app.js
45
JOURNEY THROUGH THE JAVASCRIPT MVC JUNGLE
90. https://github.com/addyosmani/todomvc/blob/master/architecture-examples/jquery/js/
app.js
91. https://github.com/derickbailey/backbone.marionette
92. https://github.com/chaplinjs/chaplin
93. https://github.com/addyosmani/backbone-aura/
94. https://github.com/walmartlabs/thorax
95. https://github.com/backbone-boilerplate/grunt-bbb
96. http://brunch.io/
46
course, templating. It should also have scaffolding tools available (em-
ber.gem, ember for brunch). Use Ember.js.
I want something more lightweight which supports live-binding
templates, routing, integration with major libraries (like jQuery and
Dojo) and is optimized for performance. It should also support a way to
implement models, views and controllers. It may not be used on as
many large public applications just yet, but has potential. Ideally, the
solution should be built by people who have previous experience creat-
ing many complex applications. Use CanJS.
I want something declarative that uses the View to derive behavior.
It focuses on achieving this through custom HTML tags and compo-
nents that specify your application intentions. It should support being
easily testable, URL management (routing) and a separation of con-
cerns through a variation of MVC. It takes a different approach to most
frameworks, providing a HTML compiler for creating your own DSL in
HTML. It may be inspired by upcoming Web platform features such as
Web Components and also has its own scaffolding tools available
(angular-seed). Use AngularJS.
I want something that offers me an excellent base for building large
scale applications. It should support a mature widget infrastructure,
modules which support lazy-loading and can be asynchronous, simple
integration with CDNs, a wide array of widget modules (graphics,
charting, grids, etc) and strong support for internationalization (i18n,
l10n). It should have support for OOP, MVC and the building blocks to
create more complex architectures. Use Dojo.
I want something which benefits from the YUI extension infrastruc-
ture. It should support models, views and routers and make it simple to
write multi-view applications supporting routing, View transitions and
more. Whilst larger, it is a complete solution that includes widgets/
components as well as the tools needed to create an organized applica-
tion architecture. It may have scaffolding tools (yuiproject), but these
need to be updated. Use YUI.
I want something simple that values asynchronous interfaces and
lack any dependencies. It should be opinionated but flexible on how to
build applications. The framework should provide bare-bones essentials
like model, view, controller, events, and routing, while still being tiny. It
should be optimized for use with CoffeeScript and come with compre-
hensive documentation. Use Spine.
I want something that will make it easy to build complex dynamic
UIs with a clean underlying data model and declarative bindings. It
should automatically update my UI on model changes using two-way
bindings and support dependency tracking of model data. I should be
able to use it with whatever framework I prefer, or even an existing
47
JOURNEY THROUGH THE JAVASCRIPT MVC JUNGLE
app. It should also come with templating built-in and be easily extensi-
ble. Use KnockoutJS.
I want something that will help me build simple Web applications
and websites. I don’t expect there to be a great deal of code involved and
so code organisation won’t be much of a concern. The solution should
abstract away browser differences so I can focus on the fun stuff. It
should let me easily bind events, interact with remote services, be ex-
tensible and have a huge plugin community. Use jQuery.
EMBER.JS
Pros: The combination of live templates and observable objects has
changed the way I write JavaScript. It can be a bit much to wrap your
head around at first, but you end up with a nice separation of responsi-
bility. I found that once I have everything set up, adding fairly complex
features only takes a couple lines of code. Without Ember, these same
features would’ve been hellish to implement.
Cons: Ember has yet to reach 1.0. Many things are still in flux, such as
the router and Ember data. The new website is very helpful, but there’s
still not as much documentation for Ember as there is for other frame-
works, specifically Backbone. Also, with so much magic in the frame-
work, it can be a little scary. There’s the fear that if something breaks
you won’t be able to figure out exactly why. Oh, and the error messages
that ember gives you often suck.
Pros:
The key factors: a) Features that let me avoid a lot of boilerplate (bindin-
gs, computer properties, view layer with the cool handlebars). b) the
core team: I’m a Rails developer and know the work of Yehuda Katz. I
trust the guy.
Cons: Documentation. It’s really sad that Ember doesn’t have good doc-
48
umentation, tutorials, screencast like Backbone, Angular or other
frameworks. Right now, we browse the code looking for docs which
isn’t ideal.
BACKBONE.JS
Pros: Simplicity—only 4 core components (Collection, Model, View,
Router). Huge community (ecosystem) and lots of solutions on Stack-
Overflow. Higher order frameworks like Marionette or Vertebrae with
lots of clever code inside. Somebody might like “low-levelness”—need
to write lots of boilerplate code, but get customized application archi-
tecture.
Cons: I don’t like how extend method works—it copies content of par-
ent objects into new one. Prototypal inheritance FTW. Sometime I miss
real world scenarios in docs examples. Also there is a lot of research
needed to figure out how to build a bigger app after reading the TODO
tutorial.
I’m missing official AMD support in projects from DocumentCloud
(BB, _). [Note: this shouldn’t be an issue with the new RequireJS shim()
method in RequireJS 2.0].
49
JOURNEY THROUGH THE JAVASCRIPT MVC JUNGLE
for v0.3. Un-aware. Whilst not a problem Backbone can fix itself, it is
certainly a major dislike associated with the framework.
I suppose in theory, you could apply this to anything else, but, Back-
bone is a recurrent one in my eyes. Hell, I’ve even seen month old arti-
cles using ancient Backbone methods and patterns.
Whatever dislikes I would have on the framework strictly itself, has
been rectified by the community through sensible hacks and approach-
es. For me, that is why Backbone is great, the community backing it up.
Pros: It’s extremely easy to get into, offering a nice gateway to MV*
based frameworks. It’s relatively customizable and there are also tons
of other people using it, making finding help or support easy.
Cons: The fact that there’s no view bindings by default (although you
can fix this). Re-rendering the whole view when a single property
changes is wasteful.
The RESTful API has a lot of positives, but the lack of bulk-saving
(admittedly a problem with REST itself, but still) and the difficulty in
getting different URI schemes to work on different types of operations
sucks.
ANGULARJS
Pros:
a) 2-way data binding is incredibly powerful. You tend to think more
about your model and the state that it is in instead of a series of events
that need to happen. The model is the single source of truth.
b) Performance. AngularJS is a small download. It’s templating uses
DOM nodes instead of converting strings into DOM nodes and should
perform better.
c) If you are targeting modern browsers and/or are a little careful,
you can drop jQuery from your dependencies too.
Cons: I’d like to be able to specify transitions for UI state changes that
propgate from a model change. Specifically for elements that use ng-
show or ng-hide I’d like to use a fade or slide in in an easy declarative
way.
Pros: It’s very intuitive, has excellent documentation. I love their data
binding approach, HTML based views, nested scopes. I switched from
50
Backbone/Thorax to Angular and never looked back. A new Chrome ex-
tension Batarang integrates with Chrome Developer’s Tools and pro-
vides live access the Angular data structures.
Cons: I’d like to have a built-in support to such functions as
drag’n’drop, however this can be added using external components
available on GitHub. I’d also like to see more 3rd party components
available for reuse. I think it’s just a matter of time for the ecosystem
around AngularJS to get more mature and then these will be available
just like they are in communities like jQuery.
Pros: It minimizes drastically the boilerplate code, allows for nice code
reuse through components, extends the HTML syntax so that many
complex features end up being as simple as applying a directive (attrib-
ute) in the HTML, and is super-easily testable thanks to a full commit-
ment to dependency injection.
You can write a non-trivial app without jQuery or without directly ma-
nipulating the DOM. That’s quite a feat.
Cons: Its learning curve is somewhat steeper than Backbone (which is
quite easy to master), but the gain is appreciative. Documentation could
be better.
KNOCKOUTJS
Pros: I don’t necessarily use it all the time, but KnockoutJS is just fan-
tastic for single page applications. Extremely easy subscribing to live
sorting; much better API for so called “collection views” in Backbone us-
ing observable arrays. And custom event on observables for effects, etc.
Cons: Feel like the API is quite hard to scale, and would probably prefer
to wrangle Backbone on the bigger applications. (But that’s also partial-
ly due to community support).
Pros: I like the data binding mechanism and feel very comfortable us-
ing it. In particular I like how they have replaced templates with control
flow binding.
Cons: I don’t like that there is no guidance or best practice in terms of
application structure. Aside from having a view model, the framework
doesn’t help you in defining a well structured view model. It’s very easy
to end up with a large unmaintainable function.
DOJO
Pros: Syntactically, Dojo is very simple. It allows for dynamic and ro-
bust builds, with the initial loader file being as low as 6k in some cases.
It is AMD compatible, making it extremely portable, and comes out-of-
51
JOURNEY THROUGH THE JAVASCRIPT MVC JUNGLE
YUI
Pros: YUI3 is a modular and use-at-will type of component library
which includes all of the goodies of Backbone and more. It even (in my
opinion) improves upon some of the concepts in Backbone by de-cou-
pling some things (i.e. attribute is a separate module that can be mixed
into any object – the event module can be mixed in similarly).
Cons: I’d love to see YUI3 support some of the auto-wiring (optional) of
Ember. I think that is really the big win for Ember; otherwise, I see YUI3
as a superior component library where I can cherry-pick what I need.
I’d also like to see a more AMD-compatible module loader. The loader
today works very well; however, it would be nicer if I could start a new
projects based on AMD modules and pull in certain YUI3 components
and other things from other places that are also using AMD.
JAVASCRIPTMVC
Pros: Has all tools included, just need to run commands and start
building. I have used for the last 6 months and it’s been really good.
Cons: The only thing I would do is to speed up development of the next
version. Developers are aware of problems and fixing issues but its go-
ing to be another ¾ months before some issues I want fixed are ad-
dressed, but then I could probably patch and do a pull request.
MARIA
Pros: Because Maria is a pure MVC framework that is focused on being
just an MVC framework. No more and no less. Its clean and simple.
Cons: A little more usage documentation outside of the source code,
52
plus a few more test cases. A tutorial that drives home the real use of
MVC with Maria would be good too.
CUJO.JS
Pros: Real apps almost never fit perfectly into an MV* box, and the
most important stuff is often outside the box. With cujo.js, you define
the box.
Yes, cujo.js has high-level MV*-like features for creating views,
models, controllers, etc., but every app is different, and no framework
can ever be a 100% solution. Rather than try to be all things, cujo.js also
provides lower level tools, architectural plumbling, and a rich plugin
system that can even be used to integrate and extend other MV* frame-
works.
Create the architecture that best suits your application, rather than
constraining your app to fit inside someone else’s predefined architec-
ture.
Cons: The broader JavaScript community is totally unprepared and un-
trained to take on large-scale applications. Most of us don’t even know
that design patterns and architectural patterns exist.
Since cujo.js is so different from other frameworks, it needs more
than a simple API reference and code snippets. Without tutorials, edu-
cational materials, and step-by-step examples, cujo.js might look
strange and overwhelming to the untrained eye but documentation is
supposed to be coming soon.
EXTJS
Pros: I think ExtJS works best in combination with Ext Designer. It
gives it an edge beyond the other GUI frameworks by letting non-pro-
grammers mock up the UI so programmers can fill in the blanks. I
think comparing it to MVC frameworks like Backbone doesn’t do it jus-
tice – its strength lies in creating rich GUIs, not lean Web apps.
For rich, commercial back-office applications I think ExtJS remains
the best choice when it comes to JavaScript solutions (i.e. not GWT etc).
For public-facing Web apps I’d rather have something that gives me
more control over the markup (and ideally something that degrades
gracefully).
Cons: It has a steeper learning curve than many of the other modern
structural frameworks. One can argue that if you’re investing in ExtJS
for the long-term this time spent learning will pay off, however I think
solutions like it should aim to better minimize the time it takes to train
teams up in using it.
53
JOURNEY THROUGH THE JAVASCRIPT MVC JUNGLE
Pros: I think a big feature of ExtJS 4 is that it throws you into the MVC
mindset and the preferred filesystem structure right from the bat. With
Dojo the initial tutorials seem to be mostly about augmenting existing
websites whereas ExtJS assumes you’re starting from scratch.
Using ExtJS doesn’t really “feel” like you’re dealing with HTML at
all. The component library is rich enough to let you go a long way with-
out touching more HTML than what is needed to bootstrap your app.
It’d be interesting to see how both compare when Web components
become more widely supported. This would finally allow manipulating
the DOM without being afraid of breaking any widgets or causing your
app’s internal state to become inconsistent.
Cons: The licensing is considered restrictive and difficult to under-
stand by some. More people would be investing in ExtJS if it was clear-
er what the upfront and long-term costs of using it are. This isn’t a con-
cern with some other structural solutions but probably isn’t as much a
worry for larger businesses.
Pros: ExtJS is a fantastic package for rapidly building out RIAs for in-
ternal use. I for one, love to build with HTML and JavaScript, and for
me there’s great satisfaction in mucking around at that level. Even
though ExtJS makes it feel like you’re not really working with HTML it
still offers a great deal of power, especially if you’re using it to create a
complex UI.
Cons: That said…I absolutely agree that it’s very heavy and I don’t
think I’d recommend it for an external facing Web application. My
biggest beef with the package overall is actually that it’s more of a PITA
to test with than I’d would like. Our tester actually ended up switching
to Sikuli because it was becoming too much of a battle trying to work
with it in Selenium.
BATMAN
Pros: It has a great and easy to use view bindings system. Plays with
Rails very nicely and is all about convention over configuration.
Cons: The documentation could be a lot better and I feel Shopify won’t
be adding the features that they say that they will.
54
In my case: I discovered that Batman.js required the least hand-writ-
ten lines of code for an implementation. I’m neither a frequent Coffee-
Script nor Batman.js user but that in itself gave me some food for
thought. Perhaps I could take some of what made this possible and
bring it over to the frameworks I do use. Or, maybe I’d simply use Bat-
man.js in a future project if I found the community and support around
it improved over time.
Regardless of whether you end up using a different solution, at the
end of the day all you have to gain from exploration is more knowledge
about what’s out there.
97. http://lostechies.com/derickbailey/2011/12/16/composite-javascript-applications-with-
backbone-and-backbone-marionette/
98. http://lostechies.com/derickbailey/2011/12/12/composite-js-apps-regions-and-region-
managers/
55
JOURNEY THROUGH THE JAVASCRIPT MVC JUNGLE
Conclusions
While there are several choices for what to use for structuring your
JavaScript Web applications these days, it’s important to be diligent in
the selection process – spend time thoroughly evaluating your options
in order to make a decision which results in sustainable, maintainable
code. Framework diversity fosters innovation, while too much similari-
ty just creates noise.
Projects like TodoMVC can help narrow down your selections to
those you feel might be the most interesting or most comfortable for a
particular project. Remember to take your time choosing, don’t feel too
constrained by using a specific pattern and keep in mind that it’s com-
pletely acceptable to build on the solution you select to best fit the
needs of your application.
99. https://github.com/derickbailey/todomvc/tree/marionette
100. http://java.sun.com/blueprints/corej2eepatterns/Patterns/
101. http://www.amazon.com/Objects-Patterns-Practice-Matt-Zandstra/dp/1590599098
102. http://dblogit.com/archives/3895
56
Experimenting with different frameworks will also give you differ-
ent views on how to solve common problems which will in turn make
you a better programmer.
Thanks to my fellow TodoMVC team-member Sindre Sorhus103 for his help with
tweaks and a technical review of this chapter. ❧
103. http://sindresorhus.com/
57
JAVASCRIPT EVENTS AND RESPONDING TO THE USER
104. http://nodejs.org/
105. http://www.onlinetools.org/articles/unobtrusivejavascript/chapter4.html
106. https://developer.mozilla.org/en/DOM/element.addEventListener
107. http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html#events-
EventTarget-addEventListener
58
Before we get into the details of events and how to use them, check
out a few demos that use scroll events in a clever way to achieve pretty
sweet results:
• jQuery Scroll Path112 is a plugin to move content along a path when the
user scrolls the page.
All of this is based on event handling and reading out what the browser
gives us. Now, let’s look at repeating the basics of that.
108. https://www.wealthfront.com/designerwanted
109. http://2011.beercamp.com/
110. http://eng.wealthfront.com/2012/03/scrolling-z-axis-with-css-3d-transforms.html
111. http://lab.hakim.se/scroll-effects/
112. http://joelb.me/scrollpath/
59
JAVASCRIPT EVENTS AND RESPONDING TO THE USER
That is a lot to play with, and the way to do that is by using addE-
ventListener() :
For example:
The element is the element that we apply the handler to; as in, “Hey
you, link! Make sure you tell me when something happens to you.” The
ajaxloader() function is the event listener; as in, “Hey you! Just stand
there and keep your ears and eyes peeled in case something happens to
the link.” Setting the useCapture to false means that we are content
to capture the event on bubbling, rather than the capturing phase. This
is a long and arduous topic113, well explained on Dev.Opera114. Let’s just
say that by setting the useCapture to false , you will be fine in
99.7434% of cases (a rough approximation). The parameter is actually
optional in all browsers but Opera.
Now, the event handler function gets an object as a parameter from
the event, which is full of awesome properties that we can play with. If
you try out my example115, you’ll see what the following code does:
113. http://www.w3.org/TR/DOM-Level-3-Events/#event-flow
114. http://dev.opera.com/articles/view/event-capture-explained/
115. http://thewebrocks.com/demos/smashing-events/eventproperties.html
60
document.addEventListener('keypress', logeventinfo,
false);
You can assign several event handlers to the same event, or the same
handler to various events (as shown in this demo).
The ev is what we get back from the event. And (again, in my case,
in Firefox) a lot of interesting things are in it:
61
JAVASCRIPT EVENTS AND RESPONDING TO THE USER
altKey: false
metaKey: false
button: 0
relatedTarget: null
mozPressure: 0
mozInputSource: 1
view: [object Window]
detail: 1
layerX: 182
layerY: 111
cancelBubble: false
explicitOriginalTarget: [object HTMLHtmlElement]
isTrusted: true
originalTarget: [object HTMLHeadingElement]
type: click
target: [object HTMLHeadingElement]
currentTarget: [object HTMLDocument]
eventPhase: 3
bubbles: true
cancelable: true
timeStamp: 574554192
defaultPrevented: false
which: 1
rangeParent: [object Text]
rangeOffset: 0
pageX: 1
pageY: 18
isChar: false
screenX: 835
screenY: 479
clientX: 1
clientY: 18
ctrlKey: false
shiftKey: false
altKey: false
metaKey: false
button: 0
relatedTarget: null
mozPressure: 0
mozInputSource: 1
view: [object Window]
62
detail: 1
layerX: 1
layerY: 18
cancelBubble: false
explicitOriginalTarget: [object Text]
isTrusted: true
It also differs from event to event. Try clicking the demo and pressing
keys, and you will see that you get different results. You can also refer
to the full list of standard event properties116.
<a class="prevent"
href="http://smashingmagazine.com">Smashing, my dear!</a>
<a class="normal"
href="http://smashingmagazine.com">Smashing, my dear!</a>
prevent.addEventListener('click', function(ev) {
alert('fabulous, really!');
ev.preventDefault();
}, false);
116. https://developer.mozilla.org/en/DOM/event#Properties
63
JAVASCRIPT EVENTS AND RESPONDING TO THE USER
normal.addEventListener('click', function(ev) {
alert('fabulous, really!');
}, false);
<ul id="resources">
<li><a href="http://developer.mozilla.org">MDN</a></li>
<li><a href="http://html5doctor.com">HTML5
Doctor</a></li>
<li><a href="http://html5rocks.com">HTML5
Rocks</a></li>
117. http://www.w3.org/TR/selectors-api/
118. https://developer.mozilla.org/En/Code_snippets/QuerySelector
119. http://thewebrocks.com/demos/smashing-events/preventdefault.html
120. http://thewebrocks.com/demos/smashing-events/eventdelegation.html
64
<li><a
href="http://beta.theexpressiveweb.com/">Expressive
Web</a></li>
<li><a
href="http://creativeJS.com/">CreativeJS</a></li>
</ul>
Hover your mouse over the list in this example and you will see that
one event handler is enough to get the links, the list item and the list it-
self. All you need to do is compare the tagName of the event target to
what you want to have.
resources.addEventListener('mouseover', showtarget,
false);
function showtarget(ev) {
var target = ev.target;
if (target.tagName === 'A') {
log.innerHTML = 'A link, with the href:' +
target.href;
}
if (target.tagName === 'LI') {
log.innerHTML = 'A list item';
}
if (target.tagName === 'UL') {
log.innerHTML = 'The list itself';
}
}
This means you can save a lot of event handlers—each of which is ex-
pensive to the browser. Instead of applying an event handler to each
link and responding that way—as most people would do in jQuery with
$(’a’).click(...) (although jQuery’s on is OK)—you can assign a
single event handler to the list itself and check which element was just
clicked.
The main benefit of this is that you are independent of the HTML. If
you add more links at a later stage, there is no need to assign new han-
dlers; the event handler will know automatically that there is a new link
to do things with.
65
JAVASCRIPT EVENTS AND RESPONDING TO THE USER
<div class="plot"></div>
.plot {
position:absolute;
background:rgb(175,50,50);
width: 20px;
height: 20px;
border-radius: 20px;
display: block;
top:0;
left:0;
}
We then assign a click handler to the document and position the ball at
PageX121 and pageY . Notice that we need to subtract half the width of
the ball in order to center it on the mouse pointer:
document.addEventListener('click', function(ev) {
plot.style.left = (ev.pageX - offset) + 'px';
plot.style.top = (ev.pageY - offset) + 'px';
}, false);
Clicking anywhere on the screen will now move the ball there. Howev-
er, it’s not smooth. If you enable the checkbox in the demo122, you will
121. https://developer.mozilla.org/en/DOM/event.pageX
122. http://thewebrocks.com/demos/smashing-events/mouseposition.html
66
see that the ball moves smoothly. We could animate this with a library,
but browsers can do better these days. All we need to do is add a transi-
tion to the CSS, and then the browser will move the ball smoothly from
one position to another. To achieve this, we define a new class named
smooth and apply it to the plot when the checkbox in the document is
clicked. The CSS:
.smooth {
-webkit-transition: 0.5s;
-moz-transition: 0.5s;
-ms-transition: 0.5s;
-o-transition: 0.5s;
transition: 0.5s;
}
The JavaScript:
var cb = document.querySelector('input[type=checkbox]');
cb.addEventListener('click', function(ev) {
plot.classList.toggle('smooth');
}, false);
The interplay between CSS and JavaScript events has always been pow-
erful, but it got even better in newer browsers. As you might have
guessed, CSS transitions and animations have their own events.
123. http://unixpapa.com/js/key.html
124. http://thewebrocks.com/demos/smashing-events/keytime.html
67
JAVASCRIPT EVENTS AND RESPONDING TO THE USER
function keydown(ev) {
if (time === 0) {
time = ev.timeStamp;
log.classList.add('animate');
}
}
function keyup(ev) {
if (time !== 0) {
log.innerHTML = ev.timeStamp - time;
time = 0;
log.classList.remove('animate');
}
}
We define the elements we want and set the time to 0 . We then apply
two event handlers to the document, one on keydown and one on
keyup .
In the keydown handler, we check whether time is 0 , and if it is, we
set time to the timeStamp of the event. We assign a CSS class to the
output element, which starts a CSS animation (see the CSS for how that
is done).
The keyup handler checks whether time is still 0 (as keydown gets
fired continuously while the key is pressed), and it calculates the differ-
ence in the time stamps if it isn’t. We set time back to 0 and remove
the class to stop the animation.
125. https://developer.mozilla.org/en/CSS/CSS_transitions
68
Check out the demo126 to see it in action. The code is simple enough.
Here is the CSS:
.plot {
background:rgb(175,50,50);
width: 20px;
height: 20px;
border-radius: 20px;
display: block;
-webkit-transition: 0.5s;
-moz-transition: 0.5s;
-ms-transition: 0.5s;
-o-transition: 0.5s;
transition: 0.5s;
}
.plot:hover {
width: 50px;
height: 50px;
border-radius: 100px;
background: blue;
}
plot.addEventListener('transitionend', function(ev) {
log.innerHTML += ev.propertyName + ':' +
ev.elapsedTime + 's ';
}, false);
This, however, works only in Firefox right now because Chrome, Safari
and Opera have vendor-prefixed events instead. As David Calhoun’s
gist127 shows, you need to detect what the browser supports and define
the event’s name that way.
CSS animation events128 work the same way, but you have three
events instead of one: animationstart , animationend and anima-
tioniteration . MDN has a demo129 of it.
126. http://thewebrocks.com/demos/smashing-events/transitionevent.html
127. https://gist.github.com/702826
128. https://developer.mozilla.org/en/CSS/CSS_animations
129. https://developer.mozilla.org/samples/cssref/animations/animevents.html
69
JAVASCRIPT EVENTS AND RESPONDING TO THE USER
document.addEventListener('mousedown', onmousedown,
false);
document.addEventListener('mouseup', onmouseup, false);
document.addEventListener('mousemove', onmousemove,
false);
function onmousedown(ev) {
if (start === 0 && x === 0 && y === 0) {
start = ev.timeStamp;
x = ev.clientX;
y = ev.clientY;
moveplot(x, y);
pressed = true;
}
}
function onmouseup(ev) {
end = ev.timeStamp;
duration = end - start;
ex = ev.clientX;
ey = ev.clientY;
mx = ex - x;
my = ey - y;
dist = Math.sqrt(mx * mx + my * my);
start = x = y = 0;
130. http://thewebrocks.com/demos/smashing-events/speeddistanceangle.html
70
pressed = false;
angle = Math.atan2( my, mx ) * 180 / Math.PI;
log.innerHTML = '<strong>' + (dist>>0) +'</strong>
pixels in <strong>'+
duration +'</strong> ms ( <strong>' +
twofloat(dist/duration) +'</strong>
pixels/ms)'+
' at <strong>' + twofloat(angle) +
'</strong> degrees';
}
function onmousemove (ev) {
if (pressed) {
moveplot(ev.pageX, ev.pageY);
}
}
function twofloat(val) {
return Math.round((val*100))/100;
}
function moveplot(x, y) {
plot.style.left = (x - offset) + 'px';
plot.style.top = (y - offset) + 'px';
}
OK, I admit: quite a lot is going on here. But it is not as hard as it looks.
For both onmousedown and onmouseup , we read the mouse’s position
with clientX and clientY and the timeStamp of the event. Mouse
events have time stamps that tell you when they happened. When the
mouse moves, all we check is whether the mouse button has been
pressed (via a boolean set in the mousedown handler) and move the plot
with the mouse.
The rest is geometry—good old Pythagoras131, to be precise. We get
the speed of the movement by checking the number of pixels traveled
in the time difference between mousedown and mouseup .
We get the number of pixels traveled as the square root of the sum
of the squares of the difference between x and y at the start and end of
the movement. And we get the angle by calculating the arctangent of
the triangle. All of this is covered in “A Quick Look Into the Math of Ani-
131. http://en.wikipedia.org/wiki/Pythagorean_theorem
71
JAVASCRIPT EVENTS AND RESPONDING TO THE USER
mations With JavaScript132”; or you can play with the following JSFid-
dle example:
Demo on JS Fiddle133.
Media Events
Both video and audio fire a lot of events that we can tap into. The most
interesting are the time events that tell you how long a song or movie
has been playing. A nice little demo to look at is the MGM-inspired di-
nosaur animation134 on MDN; I recorded a six-minute screencast135 ex-
plaining how it is done.
If you want to see a demo of all the events in action, the JPlayer
team has a great demo page showing media events136.
Input Options
Traditionally, browsers gave us mouse and keyboard interaction.
Nowadays, this is not enough because we use hardware that offers
more to us. Device orientation137, for example, allows you to respond to
the tilting of a phone or tablet; touch events138 are a big thing on mo-
biles and tablets; the Gamepad API139 allows us to read out game con-
trollers in browsers; postMessage140 allows us to send messages across
132. http://coding.smashingmagazine.com/2011/10/04/quick-look-math-animations-
javascript/
133. http://jsfiddle.net/codepo8/bAwUf/embedded/result,js,html,css
134. http://hacks.mozilla.org/2012/03/making-the-dino-roar-syncing-audio-and-css-transi-
tions/
135. http://www.youtube.com/watch?v=aFIJ_ZpV-8Q
136. http://www.jplayer.org/HTML5.Media.Event.Inspector/
137. https://developer.mozilla.org/en/DOM/DeviceOrientationEvent
138. https://developer.mozilla.org/en/DOM/Touch_events
72
domains and browser windows; pageVisibility141 allows us to react to
users switching to another tab. We can even detect when the history
object of the window has been manipulated142. Check the list of events
in the window object to find some more gems that might not be quite
ready but should be available soon for us to dig into.
Whatever comes next in browser support, you can be sure that
events will be fired and that you will be able to listen to them. The
method works and actually rocks.
139. http://hacks.mozilla.org/2011/12/paving-the-way-for-open-games-on-the-web-with-the-
gamepad-and-mouse-lock-apis/
140. https://developer.mozilla.org/en/DOM/window.postMessage
141. https://developer.mozilla.org/en/DOM/Using_the_Page_Visibility_API
142. https://developer.mozilla.org/en/DOM/window.onpopstate
73
JAVASCRIPT PROFILING WITH THE CHROME DEVELOPER TOOLS
Your website works. Now let’s make it work faster. Website perfor-
mance is about two things: how fast the page loads, and how fast the
code on it runs. Plenty of services make your website load faster, from
minimizers143 to CDNs144, but making it run faster is up to you.
Little changes in your code can have gigantic performance impacts.
A few lines here or there could mean the difference between a blazingly
fast website and the dreaded “Unresponsive Script” dialog. This chapter
shows you a few ways to find those lines of code with Chrome Develop-
er Tools145.
Establish A Baseline
We’ll look at a simple application called a color sorter, which presents a
grid of rainbow colors that you can drag and drop to mix up. Each dot is
a div tag with a little CSS to make it look like a circle.
143. http://stackoverflow.com/questions/3520285/is-there-a-good-javascript-minimizer
144. http://en.wikipedia.org/wiki/Content_delivery_network
145. http://code.google.com/chrome/devtools/docs/overview.html
74
Generating my rainbow colors was a little tricky, so I got help from
“Making Annoying Rainbows in JavaScript146.”
The page loads pretty fast, but it still takes a moment and blinks a
little before it paints. Time to profile the page and make it faster.
Always start performance-improvement projects with a baseline un-
derstanding of how fast or slow your application already is. The base-
line will let you know whether you’re making improvements and help
you make tradeoffs. For this chapter, we’ll use Chrome Developer
Tools147.
The profiler is part of Chrome Developer Tools, which is always
available in Chrome. Click the “Tools” menu under the little wrench to
open it. Firebug148 has some profiling tools, too, but the WebKit
browsers (Chrome and Safari) are best at profiling code and showing
timelines. Chrome also offers an excellent tool for event tracing, called
Speed Tracer149.
To establish our baseline, we’ll start recording in the “Timeline” tab,
load our page and then stop the recording. (To start recording once
146. http://krazydad.com/tutorials/makecolors.php
147. http://code.google.com/chrome/devtools/docs/overview.html
148. http://getfirebug.com
149. https://developers.google.com/web-toolkit/speedtracer/
75
JAVASCRIPT PROFILING WITH THE CHROME DEVELOPER TOOLS
Chrome Developer Tools is open, click the “Timeline” tab, and then the
small black circle icon for “Record” at the very bottom of the window.)
Chrome is smart about not starting to record until the page starts to
load. I run it three times and take the average, in case my computer
runs slowly during the first test.
My average baseline—i.e. the time between the first request for the
page and the final painting of the page in the browser—is 1.25 seconds.
That’s not bad, but it’s not great for such a small page.
I want to make my code run faster, but I’m not sure what’s making it
slow. The profiler helps me find out.
Create A Profile
The timeline tells us how long our code took to run, but that doesn’t
help us know what’s going on while it’s running. We could make
changes and run the timeline again and again, but that’s just shooting
in the dark. The “Profiles” tab gives us a better way to see what’s going
on.
76
Profilers show us which functions take the most time. Let’s make our
baseline profile by switching to the “Profiles” tab in Chrome Developer
Tools, where three types of profiling are offered:
3. Heap snapshot
Shows how memory is being used by our JavaScript objects.
We want to make our JavaScript run faster, so we’ll use the CPU profil-
ing. We start the profile, refresh the page and then stop the profiler.
The first thing that’s clear from the profile is that a lot is going on. The
color sorter uses jQuery and jQuery UI, which are doing a lot of stuff
like managing plugins and parsing regular expressions. I can also see
that two of my functions are at the top of the list: decimalToHex and
77
JAVASCRIPT PROFILING WITH THE CHROME DEVELOPER TOOLS
$(document).ready(function() {
makeColorSorter(.05, .05, .05, 0, 2, 4, 128, 127,
121);
makeSortable();
});
Knowing where they’re called from also makes clear that making the
colors sortable isn’t my biggest performance problem. Performance is-
sues resulting from the addition of a lot of sortables is common150, but
my code is taking more time to add DOM elements than to make them
sortable.
I want to start making those functions faster, but first I want to iso-
late my changes. A lot happens when the page loads, and I want to get
all of that out of my profile.
function testColorSorter() {
makeColorSorter(.05, .05, .05, 0, 2, 4, 128, 127,
121);
makeSortable();
}
150. http://37signals.com/svn/posts/3137-using-event-capturing-to-improve-basecamp-page-
load-times
151. http://zgrossbart.github.com/jsprofarticle/index2.htm
78
<button id="clickMe" onclick="testColorSorter();">Click
me</button>
The first thing to notice is that the decimalToHex function is now tak-
ing up 4.23% of the time to load; it’s what the code spends the most time
on. Let’s create a new baseline to see how much the code improves in
this scenario.
79
JAVASCRIPT PROFILING WITH THE CHROME DEVELOPER TOOLS
A few events occur before I press the button, but I only care about how
long it took between the times the mouse was clicked and the browser
painted the color sorter. The mouse button was clicked at 390 millisec-
onds, and the paint event happened at 726 milliseconds; 726 minus 390
equals my baseline of 336 milliseconds. Just as with the first baseline, I
ran it three times and took the average time.
At this point, I know where to look and how long the code takes to
run. Now we’re ready to start fixing the problem.
Make It Faster
The profiler only tells us which function is causing the problem, so we
need to look into it and understand what it does.
function decimalToHex(d) {
var hex = Number(d).toString(16);
hex = "00".substr(0, 2 - hex.length) + hex;
80
console.log('converting ' + d + ' to ' + hex);
return hex;
}
Each dot in the color sorter takes a background color value in hex for-
mat152, such as #86F01B or #2456FE . These values represent the red,
green and blue values of the color. For example,
function decimalToHex(d) {
var hex = Number(d).toString(16);
return hex.length === 1 ? '0' + hex : hex; }
Version three154 of the color sorter changes the string only when it
needs the padding and doesn’t have to call substr . With this new func-
tion, our runtime is 137 milliseconds. By profiling the code again, I can
see that the decimalToHex function now takes only 0.04% of the total
time—putting it way down the list.
152. http://en.wikipedia.org/wiki/Hexadecimal
153. http://en.wikipedia.org/wiki/Rgb
154. http://zgrossbart.github.com/jsprofarticle/index3.htm
81
JAVASCRIPT PROFILING WITH THE CHROME DEVELOPER TOOLS
We can also see that the function using the most CPU is
e.extend.merge from jQuery. I’m not sure what that function does be-
cause the code is minimized. I could add the development version of
jQuery, but I can see that the function is getting called from makeCol-
orSorter , so let’s make that one faster next.
82
{
var red = Math.floor(Math.sin(frequency1 * i +
phase1) * width + center);
var green = Math.floor(Math.sin(frequency2 * i +
phase2) * width + center);
var blue = Math.floor(Math.sin(frequency3 * i +
phase3) * width + center);
}
}
We could take out more console.log functions. The calls are especial-
ly bad because each is also calling the decimalToHex function, which
means that decimalToHex is effectively being called twice as often as it
should.
This function changes the DOM a lot. Every time the loop runs, it
adds a new div to the colors div tag. This makes me wonder whether
that’s what the e.extend.merge function was doing. The profiler
makes it easy to tell with a simple experiment.
Instead of adding a new div each time the loop runs, I want to add
all of the div tags at once. Let’s create a variable to hold them, and then
add them once at the end.
83
JAVASCRIPT PROFILING WITH THE CHROME DEVELOPER TOOLS
$('#colors').append(colors);
}
This small change in the code means that the DOM changes once,
when it adds all of the div tags. Testing that with the timeline, we see
that the runtime between the click and the paint events is now 31 mil-
liseconds. This one DOM change has brought the time for version
four155 down by about 87%. We can also run the profiler again and see
that the e.extend.merge function now takes up such a small percent-
age of the time that it doesn’t show up on the list.
We could make the code one notch faster by removing the decimal-
ToHex function entirely. CSS supports RGB colors, so we don’t need to
convert them to hex. Now we can write our makeColorSorter function
like this:
155. http://zgrossbart.github.com/jsprofarticle/index4.htm
84
var blue = Math.floor(Math.sin(frequency3 * i +
phase3) * width + center);
$('#colors').append(colors);
}
Version five156 runs in only 26 milliseconds and uses 18 lines of code for
what used to take 28 lines.
2. Isolate the problem from any other code running in the application.
1. Start with the slowest parts first so that you get the most improve-
ment for the time spent tuning.
Everyone wants their website to run faster. You have to develop new
features, but new features usually make a website run slower. So, in-
vesting time in tuning the performance does pay off.
156. http://zgrossbart.github.com/jsprofarticle/index5.htm
85
JAVASCRIPT PROFILING WITH THE CHROME DEVELOPER TOOLS
Profiling and tuning cut the final color sorter157’s runtime by over 92%.
How much faster could your website be? ❧
157. http://zgrossbart.github.com/jsprofarticle/index6.htm
86
Writing Fast,
Memory-Efficient
JavaScript
BY ADDY OSMANI ❧
• What is the engine unable to optimize for, and is the garbage collector
able to clean up what I’m expecting it to?
158. http://code.google.com/p/v8/
159. http://www.html5rocks.com/en/tutorials/speed/v8/
160. https://developer.mozilla.org/en-US/docs/SpiderMonkey
161. http://my.opera.com/ODIN/blog/carakan-faq
162. http://en.wikipedia.org/wiki/Chakra_(JScript_engine)
87
WRITING FAST, MEMORY-EFFICIENT JAVASCRIPT
Fast-loading Web sites—like fast cars—require the use specialized tools. Image source:
dHybridcars163.
• The runtime profiler monitors the system being run and identifies
“hot” functions (i.e. code that ends up spending a long time running).
163. http://dhybridcars.com/toyota-hybrid/2013-scion-fr-s-sexy-sport-car/media/2013-scion-
fr-s-speed-gauge-img-8/
164. https://developers.google.com/v8/design
88
• An optimizing compiler recompiles and optimizes the “hot” code iden-
tified by the runtime profiler, and performs optimizations such as in-
lining (i.e. replacing a function call site with the body of the callee).
Garbage Collection
Garbage collection is a form of memory management. It’s where we
have the notion of a collector which attempts to reclaim memory occu-
pied by objects that are no longer being used. In a garbage-collected lan-
guage such as JavaScript, objects that are still referenced by your appli-
cation are not cleaned up.
Manually de-referencing objects is not necessary in most cases. By
simply putting the variables where they need to be (ideally, as local as
possible, i.e. inside the function where they are used versus an outer
scope), things should just work.
89
WRITING FAST, MEMORY-EFFICIENT JAVASCRIPT
DE-REFERENCING MISCONCEPTIONS
In quite a few discussions online about reclaiming memory in
JavaScript, the delete keyword is brought up, as although it was sup-
posed to be used for just removing keys from a map, some developers
think you can force de-referencing using it. Avoid using delete if you
can. In the below example, delete o.x does a lot more harm than good
behind the scenes, as it changes o ‘s hidden class and makes it a generic
slow object.
var o = { x: 1 };
delete o.x; // true
o.x; // undefined
That said, you are almost certain to find references to delete in many
popular JavaScript libraries – it does have a purpose in the language.
The main takeaway here is to avoid modifying the structure of hot ob-
jects at runtime. JavaScript engines can detect such “hot” objects and
attempt to optimize them. This is easier if the object’s structure doesn’t
heavily change over its lifetime and delete can trigger such changes.
There are also misconceptions about how null works. Setting an
object reference to null doesn’t “null” the object. It sets the object refer-
ence to null . Using o.x = null is better than using delete , but it’s
probably not even necessary.
var o = { x: 1 };
o = null;
o; // null
o.x // TypeError
If this reference was the last reference to the object, the object is then
eligible for garbage collection. If the reference was not the last refer-
ence to the object, the object is reachable and will not be garbage col-
lected.
Another important note to be aware of is that global variables are
not cleaned up by the garbage collector during the life of your page. Re-
165. http://www.flickr.com/photos/26817893@N05/2864644153/
90
gardless of how long the page is open, variables scoped to the JavaScript
runtime global object will stick around.
Globals are cleaned up when you refresh the page, navigate to a differ-
ent page, close tabs or exit your browser. Function-scoped variables get
cleaned up when a variable falls out of scope. When functions have ex-
ited and there aren’t any more references to it, the variable gets cleaned
up.
RULES OF THUMB
To give the garbage collector a chance to collect as many objects as pos-
sible as early as possible, don’t hold on to objects you no longer
need. This mostly happens automatically; here are a few things to keep
in mind.
• Ensure that you’re unbinding event listeners where they are no longer
required, especially when the DOM objects they’re bound to are about
to be removed
• If you’re using a data cache locally, make sure to clean that cache or use
an aging mechanism to avoid large chunks of data being stored that
you’re unlikely to reuse
FUNCTIONS
Next, let’s look at functions. As we’ve already said, garbage collection
works by reclaiming blocks of memory (objects) which are no longer
reachable. To better illustrate this, here are some examples.
function foo() {
var bar = new LargeObject();
bar.someCall();
}
91
WRITING FAST, MEMORY-EFFICIENT JAVASCRIPT
function foo() {
var bar = new LargeObject();
bar.someCall();
return bar;
}
// somewhere else
var b = foo();
We now have a reference to the object which survives the call and per-
sists until the caller assigns something else to b (or b goes out of
scope).
CLOSURES
When you see a function that returns an inner function, that inner
function will have access to the outer scope even after the outer func-
tion is executed. This is basically a closure166—an expression which can
work with variables set within a specific context. For example:
// Usage
var sumA = sum(4);
var sumB = sumA(3);
console.log(sumB); // Returns 7
The function object created within the execution context of the call to
sum can’t be garbage collected, as it’s referenced by a global variable and
is still very much accessible. It can still be executed via sumA(n) .
Let’s look at another example. Here, can we access largeStr ?
166. http://robertnyman.com/2008/10/09/explaining-javascript-scope-and-closures/
92
var a = function () {
var largeStr = new Array(1000000).join('x');
return function () {
return largeStr;
};
}();
Yes, we can, via a() , so it’s not collected. How about this one?
var a = function () {
var smallStr = 'x';
var largeStr = new Array(1000000).join('x');
return function (n) {
return smallStr;
};
}();
TIMERS
One of the worst places to leak is in a loop, or in setTimeout() / set-
Interval() , but this is quite common. Consider the following example.
var myObj = {
callMeMaybe: function () {
var myRef = this;
var val = setTimeout(function () {
console.log('Time is running out!');
myRef.callMeMaybe();
}, 1000);
}
};
If we then run:
myObj.callMeMaybe();
to begin the timer, we can see every second “Time is running out!” If we
then run:
myObj = null;
93
WRITING FAST, MEMORY-EFFICIENT JAVASCRIPT
The timer will still fire. myObj won’t be garbage collected as the closure
passed to setTimeout has to be kept alive in order to be executed. In
turn, it holds references to myObj as it captures myRef . This would be
the same if we’d passed the closure to any other function, keeping ref-
erences to it.
It is also worth keeping in mind that references inside a setTime-
out / setInterval call, such as functions, will need to execute and com-
plete before they can be garbage collected.
Doing too much can be as harmful as not doing anything. Image source: Tim Sheerman-
Chase167.
167. http://www.flickr.com/photos/tim_uk/7717078488/sizes/c/in/photostream/
94
• Adds event handlers for toggling a class when a user clicks on any cell.
There are a few different factors to this problem, even though it’s quite
straightforward to solve. How do we store the data? How do we effi-
ciently draw the table and append it to the DOM? How do we handle
events on this table optimally?
A first (naive) take on this problem might be to store each piece of
available data in an object which we group into an array. One might use
jQuery to iterate through the data and draw the table, then append it to
the DOM. Finally, one might use event binding for adding the click be-
havior we desire.
Note: This is NOT what you should be doing
addTable: function () {
for (var i = 0; i < rows; i++) {
$tr = $('<tr></tr>');
for (var j = 0; j < this.data.length;
j++) {
$tr.append('<td>' +
this.data[j]['id'] + '</td>');
}
$tr.appendTo($tbody);
}
},
addEvents: function () {
$('table td').on('click', function () {
$(this).toggleClass('active');
});
}
};
}();
Simple, but it gets the job done. In this case however, the only data
we’re iterating are IDs, a numeric property which could be more simply
represented in a standard array. Interestingly, directly using Document-
95
WRITING FAST, MEMORY-EFFICIENT JAVASCRIPT
Fragment and native DOM methods are more optimal than using
jQuery (in this manner) for our table generation, and of course, event
delegation is typically more performant than binding each td individu-
ally.
Note that jQuery does use DocumentFragment internally behind the
scenes, but in our example, the code is calling append() within a loop
and each of these calls has little knowledge of the other so it may not be
able to optimize for this example. This should hopefully not be a pain
point, but be sure to benchmark your own code to be sure.
In our case, adding in these changes results in some good (expected)
performance gains. Event delegation provides decent improvement
over simply binding, and opting for documentFragment 168 was a real
booster.
td.appendChild(document.createTextNode(this.data[j]));
frag2.appendChild(td);
}
tr.appendChild(frag2);
frag.appendChild(tr);
}
168. http://jsperf.com/first-pass
96
tbody.appendChild(frag);
},
addEvents: function () {
$('table').on('click', 'td', function () {
$(this).toggleClass('active');
});
}
};
}();
moduleG.prototype.data = dataArray;
moduleG.prototype.init = function () {
this.addTable();
this.addEvents();
};
moduleG.prototype.addTable = function () {
var template = _.template($('#template').text());
var html = template({'data' : this.data});
$tbody.append(html);
};
moduleG.prototype.addEvents = function () {
$('table').on('click', 'td', function () {
$(this).toggleClass('active');
});
};
As it turns out, in this case the performance benefits are negligible. Opt-
ing for templating and prototypes169 didn’t really offer anything more
than what we had before. That said, performance isn’t really the reason
169. http://jsperf.com/second-pass
97
WRITING FAST, MEMORY-EFFICIENT JAVASCRIPT
V8 Optimization Tips
Whilst detailing every V8 optimization is outside the scope of this
chapter, there are certainly many tips worth noting. Keep these in mind
and you’ll reduce your chances of writing unperformant code.
• If you care about speed, try very hard to keep your functions monomor-
phic, i.e. make sure that variables (including properties, arrays and
function parameters) only ever contain objects with the same hidden
class. For example, don’t do this:
function add(x, y) {
return x+y;
}
add(1, 2);
add('a','b');
add(my_custom_object, undefined);
170. http://jsperf.com/canvas-drawimage-vs-webgl-drawarrays/6
171. http://jsperf.com/canvas-pixel-manipulation/30
172. http://jsperf.com/typed-arrays-for-pixel-manipulation
173. http://jsperf.com/dom-vs-innerhtml-based-templating/473
174. http://jsperf.com/javascript-templating-shootoff-extended/26
98
• Don’t load from uninitialized or deleted elements. This won’t make a
difference in output, but it will make things slower.
For more tips, watch Daniel Clifford’s Google I/O talk Breaking the
JavaScript Speed Limit with V8175 as it covers these topics well. Opti-
mizing For V8—A Series176 is also worth a read.
• Properties on objects are quite complex: they can be created with set-
ters, and with differing enumerability and writability. Items in arrays
aren’t able to be customized as heavily—they either exist or they don’t.
At an engine level, this allows for more optimization in terms of orga-
nizing the memory representing the structure. This is particularly ben-
eficial when the array contains numbers. For example, when you need
vectors, don’t define a class with properties x, y, z; use an array instead..
There’s really only one major difference between objects and arrays in
JavaScript, and that’s the arrays’ magic length property. If you’re keep-
ing track of this property yourself, objects in V8 should be just as fast
as arrays.
• Create objects using a constructor function. This ensures that all ob-
jects created with it have the same hidden class and helps avoid chang-
175. http://www.youtube.com/watch?v=UJPdhx5zTaw
176. http://floitsch.blogspot.co.uk/2012/03/optimizing-for-v8-introduction.html
177. http://jsperf.com/performance-of-array-vs-object/3
99
WRITING FAST, MEMORY-EFFICIENT JAVASCRIPT
ing these classes. As an added benefit, it’s also slightly faster than Ob-
ject.create() 178
Object Cloning
Object cloning is a common problem for app developers. While it’s pos-
sible to benchmark how well various implementations work with this
type of problem in V8, be very careful when copying anything. Copying
big things is generally slow—don’t do it. for..in loops in JavaScript
are particularly bad for this, as they have a devilish specification and
will likely never be fast in any engine for arbitrary objects.
When you absolutely do need to copy objects in a performance-criti-
cal code path (and you can’t get out of this situation), use an array or a
custom “copy constructor” function which copies each property explic-
itly. This is probably the fastest way to do it:
function clone(original) {
this.foo = original.foo;
this.bar = original.bar;
}
var copy = new clone(original);
178. http://jsperf.com/object-create-vs-constructor-vs-object-literal/7
100
Performance improvements when using the module or prototypal patterns.
// Prototypal pattern
Klass1 = function () {}
Klass1.prototype.foo = function () {
log('foo');
}
Klass1.prototype.bar = function () {
log('bar');
}
// Module pattern
Klass2 = function () {
var foo = function () {
log('foo');
},
bar = function () {
log('bar');
};
return {
foo: foo,
179. http://jsperf.com/prototypal-performance/12
101
WRITING FAST, MEMORY-EFFICIENT JAVASCRIPT
bar: bar
}
}
Klass3 = function () {
return {
foo: FooFunction,
bar: BarFunction
}
}
// Iteration tests
// Prototypal
var i = 1000,
objs = [];
while (i--) {
var o = new Klass1()
objs.push(new Klass1());
o.bar;
o.foo;
}
// Module pattern
var i = 1000,
objs = [];
while (i--) {
var o = Klass2()
objs.push(Klass2());
o.bar;
o.foo;
102
}
Note: If you don’t require a class, avoid the trouble of creating one.
Here’s180 an example of how to gain performance boosts by removing
the class overhead altogether.
Array Literals
Array literals are useful because they give a hint to the VM about the
size and type of the array. They’re typically good for small to medium
sized arrays.
// Don't do this:
a = []; // Here V8 knows nothing about the array
for(var i = 1; i <= 4; i++) {
a.push(i);
}
180. http://jsperf.com/prototypal-performance/54
103
WRITING FAST, MEMORY-EFFICIENT JAVASCRIPT
181. http://jsperf.com/type-inference-performance/2
182. http://jsperf.com/sparse-arrays-vs-full-arrays
183. http://jsperf.com/packed-vs-holey-arrays
104
Test of empty literal versus pre-allocated array in various browsers.
// Empty array
var arr = [];
for (var i = 0; i < 1000000; i++) {
arr[i] = i;
}
// Pre-allocated array
var arr = new Array(1000000);
for (var i = 0; i < 1000000; i++) {
arr[i] = i;
}
184. http://jsperf.com/pre-allocated-arrays
105
WRITING FAST, MEMORY-EFFICIENT JAVASCRIPT
is why squeezing every drop of extra performance you can out of code
can sometimes be critical.
Some of the tools and techniques recommended below can assist with
this process.
BENCHMARKING
There are many ways to run benchmarks on JavaScript snippets to test
their performance—the general assumption being that benchmarking
is simply comparing two timestamps. One such pattern was pointed
185. http://www.flickr.com/photos/perolofforsberg/6691744587/in/photostream/
106
out by the jsPerf186 team, and happens to be used in SunSpider187‘s and
Kraken188‘s benchmark suites:
var totalTime,
start = new Date,
iterations = 1000;
while (iterations--) {
// Code snippet goes here
}
// totalTime → the number of milliseconds taken
// to execute the code snippet 1000 times
totalTime = new Date - start;
Here, the code to be tested is placed within a loop and run a set number
of times (e.g. six). After this, the start date is subtracted from the end
date to find the time taken to perform the operations in the loop.
However, this oversimplifies how benchmarking should be done, es-
pecially if you want to run the benchmarks in multiple browsers and
environments. Garbage collection itself can have an impact on your re-
sults. Even if you’re using a solution like window.performance , you
still have to account for these pitfalls.
Regardless of whether you are simply running benchmarks against
parts of your code, writing a test suite or coding a benchmarking li-
brary, there’s a lot more to JavaScript benchmarking than you might
think. For a more detailed guide to benchmarking, I highly recommend
reading JavaScript Benchmarking189 by Mathias Bynens and John-
David Dalton.
PROFILING
The Chrome Developer Tools have good support for JavaScript profil-
ing190. You can use this feature to detect what functions are eating up
the most of your time so that you can then go optimize them. This is
important, as even small changes to your codebase can have serious im-
pacts on your overall performance.
186. http://jsperf.com
187. http://www.webkit.org/perf/sunspider/sunspider.html
188. http://krakenbenchmark.mozilla.org/
189. http://mathiasbynens.be/notes/javascript-benchmarking
190. https://developers.google.com/chrome-developer-tools/docs/profiles
107
WRITING FAST, MEMORY-EFFICIENT JAVASCRIPT
Profiling starts with obtaining a baseline for your code’s current perfor-
mance, which can be discovered using the Timeline. This will tell us
how long our code took to run. The Profiles tab then gives us a better
view into what’s happening in our application. The JavaScript CPU pro-
file shows us how much CPU time is being used by our code, the CSS
selector profile shows us how much time is spent processing selectors
and Heap snapshots show how much memory is being used by our ob-
jects.
Using these tools, we can isolate, tweak and reprofile to gauge
whether changes we’re making to specific functions or operations are
improving performance.
The Profile tab gives you information about your code’s performance.
108
For a good introduction to profiling, read JavaScript Profiling With The
Chrome Developer Tools191, by Zack Grossbart.
Tip: Ideally, you want to ensure that your profiling isn’t being affect-
ed by extensions or applications you’ve installed, so run Chrome using
the --user-data-dir <empty_directory> flag. Most of the time, this
approach to optimization testing should be enough, but there are times
when you need more. This is where V8 flags can be of help.
Some of the memory statistics that our teams care about include pri-
vate memory usage, JavaScript heap size, DOM node counts, storage
clearing, event listener counts and what’s going on with garbage collec-
tion. For those familiar with event-driven architectures, you might be
interested to know that one of the most common issues we used to
have were listen() ’s without unlisten() ’s (Closure) and missing
dispose() ’s for objects that create event listeners.
Luckily the DevTools can help locate some of these issues, and
Loreena Lee has a fantastic presentation available documenting the “3
191. http://coding.smashingmagazine.com/2012/06/12/javascript-profiling-chrome-
developer-tools/
109
WRITING FAST, MEMORY-EFFICIENT JAVASCRIPT
192. https://docs.google.com/presentation/d/1wUVmf78gG-ra5aOxvTfYdiLkdGaR9OhXRnO-
lIcEmu2s/pub?start=false&loop=false&delayms=3000#slide=id.g1d65bdf6_0_0
193. https://github.com/documentcloud/backbone/blob/master/backbone.js#L1234
194. https://github.com/documentcloud/backbone/blob/master/backbone.js#L1235
195. https://github.com/emberjs/ember.js/blob/d8f76a7fdde741ae3d1e07b12df9cb6718170e48/
packages/ember-handlebars/lib/helpers/binding.js#L296
110
In his article, Derick covers many of the common memory pitfalls196
when working with Backbone.js and how to fix them.
There is also a helpful tutorial available for debugging memory
leaks in Node197 by Felix Geisendörfer worth reading, especially if it
forms a part of your broader SPA stack.
MINIMIZING REFLOWS
When a browser has to recalculate the positions and geometrics of ele-
ments in a document for the purpose of re-rendering it, we call this re-
flow198. Reflow is a user-blocking operation in the browser, so it’s help-
ful to understand how to improve reflow time.
You should batch methods that trigger reflow199 or that repaint, and
use them sparingly. It’s important to process off DOM where possible.
This is possible using DocumentFragment200, a lightweight document
object. Think of it as a way to extract a portion of a document’s tree, or
create a new “fragment” of a document. Rather than constantly adding
to the DOM using nodes, we can use document fragments to build up
all we need and only perform a single insert into the DOM to avoid ex-
cessive reflow.
196. http://lostechies.com/derickbailey/2012/03/19/backbone-js-and-javascript-garbage-collec-
tion/
197. https://github.com/felixge/node-memory-leak-tutorial
198. https://www.youtube.com/watch?feature=player_embedded&v=ZHxbs5WEQzE
199. http://stackoverflow.com/questions/510213/when-does-reflow-happen-in-a-dom-envi-
ronment
200. http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-B63ED1A3
111
WRITING FAST, MEMORY-EFFICIENT JAVASCRIPT
function addDivs(element) {
var div;
for (var i = 0; i < 20; i ++) {
div = document.createElement('div');
div.innerHTML = 'Heya!';
element.appendChild(div);
}
}
function addDivs(element) {
var div;
// Creates a new empty DocumentFragment.
var fragment = document.createDocumentFragment();
for (var i = 0; i < 20; i ++) {
div = document.createElement('a');
div.innerHTML = 'Heya!';
fragment.appendChild(div);
}
element.appendChild(fragment);
}
You can read more about this topic at Make the Web Faster201,
JavaScript Memory Optimization202 and Finding Memory Leaks203.
201. https://developers.google.com/speed/articles/javascript-dom
202. http://blog.tojicode.com/2012/03/javascript-memory-optimization-and.html
203. http://gent.ilcore.com/2011/08/finding-memory-leaks.html
112
and retrieves heap snapshots and detects what objects are causing
leaks.
There’s a whole post on how to use the tool204, and I encourage you to
check it out or view the Leak Finder project page205.
Some more information: In case you’re wondering why a tool like
this isn’t already integrated with our Developer Tools, the reason is
twofold. It was originally developed to help us catch some specific
memory scenarios in the Closure Library, and it makes more sense as
an external tool (or maybe even an extension if we get a heap profiling
extension API in place).
204. http://google-opensource.blogspot.de/2012/08/leak-finder-new-tool-for-javascript.html
205. http://code.google.com/p/leak-finder-for-javascript/
113
WRITING FAST, MEMORY-EFFICIENT JAVASCRIPT
• trace-opt – log names of optimized functions and show where the op-
timizer is skipping code because it can’t figure something out.
High Resolution Time (HRT) provides the current time in sub-millisecond resolution.
206. http://mrale.ph/blog/2011/12/18/v8-optimization-checklist.html
207. http://www.w3.org/TR/hr-time/
208. http://updates.html5rocks.com/2012/08/When-milliseconds-are-not-enough-
performance-now
209. http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html
114
surements that are recorded while a webpage is loaded and presented
to the user. Timing information is exposed via win-
dow.performance.timing , which you can simply use in the console:
Looking at the data above, we can extract some very useful informa-
tion. For example, network latency is responseEnd-fetchStart , the
time taken for a page load once it’s been received from the server is
loadEventEnd-responseEnd and the time taken to process between
navigation and page load is loadEventEnd-navigationStart .
As you can see above, a perfomance.memory property is also avail-
able that gives access to JavaScript memory usage data such as the total
heap size.
For more details on the Navigation Timing API, read Sam Dutton’s
great article Measuring Page Load Speed With Navigation Timing210.
210. http://www.html5rocks.com/en/tutorials/webperformance/basics/
115
WRITING FAST, MEMORY-EFFICIENT JAVASCRIPT
What’s really useful about this tool is that it allows you to capture pro-
filing data about what Chrome is doing under the hood, so you can
properly adjust your JavaScript execution, or optimize your asset load-
ing.
Lilli Thompson has an excellent write-up for games developers211 on
using about:tracing to profile WebGL games. The write-up is also
useful for general JavaScripters.
Navigating to about:memory in Chrome is also useful as it shows
the exact amount of memory being used by each tab, which is helpful
for tracking down potential leaks.
Conclusion
As we’ve seen, there are many hidden performance gotchas in the world
of JavaScript engines, and no silver bullet available to improve perfor-
mance. It’s only when you combine a number of optimizations in a
(real-world) testing environment that you can realize the largest perfor-
mance gains. But even then, understanding how engines interpret and
optimize your code can give you insights to help tweak your applica-
tions.
Measure It. Understand it. Fix it. Rinse and repeat.
211. http://www.html5rocks.com/en/tutorials/games/abouttracing/
116
Image source: Sally Hunter212.
212. http://www.flickr.com/photos/38891164@N02/4266609887/
117
DESIGNING BETTER JAVASCRIPT APIS
At some point or another, you will find yourself writing JavaScript code
that exceeds the couple of lines from a jQuery plugin. Your code will do
a whole lot of things; it will (ideally) be used by many people who will
approach your code differently. They have different needs, knowledge
and expectations.
This chapter covers the most important things that you will need to
consider before and while writing your own utilities and libraries. We’ll
focus on how to make your code accessible to other developers. A couple
of topics will be touching upon jQuery for demonstration, yet this chap-
ter is neither about jQuery nor about writing plugins for it.
Peter Drucker once said: “The computer is a moron.” Don’t write
code for morons, write for humans! Let’s dive into designing the APIs
that developers will love using.
118
Fluent Interface
The Fluent Interface213 is often referred to as Method Chaining (although
that’s only half the truth). To beginners it looks like the jQuery style.
While I believe the API style was a key ingredient in jQuery’s success, it
wasn’t invented by them—credits seem to go to Martin Fowler who
coined the term214 back in 2005, roughly a year before jQuery was re-
leased. Fowler only gave the thing a name, though—Fluent Interfaces
have been around for a much longer time.
Aside from major simplifications, jQuery offered to even out severe
browser differences. It has always been the Fluent Interface that I have
loved most about this extremely successful library. I have come to en-
joy this particular API style so much that it became immediately appar-
ent that I wanted this style for URI.js215, as well. While tuning up the
URI.js API, I constantly looked through the jQuery source to find the lit-
tle tricks that would make my implementation as simple as possible. I
found out that I was not alone in this endeavor. Lea Verou216 created
chainvas217—a tool to wrap regular getter/setter APIs into sweet fluent
interfaces. Underscore’s _.chain() 218 does something similar. In fact,
most of the newer generation libraries support method chaining.
METHOD CHAINING
The general idea of Method Chaining219 is to achieve code that is as flu-
ently readable as possible and thus quicker to understand. With Method
Chaining we can form code into sentence-like sequences, making code
easy to read, while reducing noise in the process:
213. http://en.wikipedia.org/wiki/Fluent_interface#JavaScript
214. http://martinfowler.com/bliki/FluentInterface.html
215. http://medialize.github.com/URI.js/
216. https://twitter.com/leaverou
217. http://lea.verou.me/chainvas/
218. http://underscorejs.org/#chain
219. http://en.wikipedia.org/wiki/Method_chaining
119
DESIGNING BETTER JAVASCRIPT APIS
// CQS - command
$elem.setCss("background", "green");
// CQS - query
$elem.getCss("color") === "red";
// non-CQS - command
$elem.css("background", "green");
// non-CQS - query
$elem.css("color") === "red";
As you can see, getter and setter methods are merged into a single
method. The action to perform (namely, query or command) is decided
220. http://en.wikipedia.org/wiki/Command-query_separation
221. http://api.jquery.com/css/
120
by the amount of arguments passed to the function, rather than by
which function was called. This allows us to expose fewer methods and
in turn type less to achieve the same goal.
It is not necessary to compress getters and setters into a single
method in order to create a fluid interface—it boils down to personal
preference. Your documentation should be very clear with the approach
you’ve decided on. I will get into documenting APIs later, but at this
point I would like to note that multiple function signatures may be
harder to document.
GOING FLUENT
While method chaining already does most of the job for going fluent,
you’re not off the hook yet. To illustrate the next step of fluent, we’re
pretending to write a little library handling date intervals. An interval
starts with a date and ends with a date. A date is not necessarily con-
nected to an interval. So we come up with this simple constructor:
While this looks right at first glance, this example shows what’s wrong:
121
DESIGNING BETTER JAVASCRIPT APIS
Array.prototype.slice.call(arguments, 0)
);
}
As you can see in this last example, there are less variables to declare,
less code to write, and the operation almost reads like an english sen-
tence. With this example you should have realized that method chain-
ing is only a part of a fluent interface, and as such, the terms are not
synonymous. To provide fluency, you have to think about code
streams—where are you coming from and where you are headed.
This example illustrated fluidity by extending a native object with a
custom function. This is as much a religion as using semicolons or not.
In Extending built-in native objects. Evil or not?222 kangax223 explains
the ups and downs of this approach. While everyone has their opinions
about this, the one thing everybody agrees on is keeping things consis-
tent. As an aside, even the followers of “Don’t pollute native objects
with custom functions” would probably let the following, still some-
what fluid trick slide:
String.prototype.foo = function() {
return new Foo(this);
}
222. http://perfectionkills.com/extending-built-in-native-objects-evil-or-not/
223. https://twitter.com/kangax
122
"I'm a native object".foo()
.iAmACustomFunction();
With this approach your functions are still within your namespace, but
made accessible through another object. Make sure your equivalent of
.foo() is a non-generic term, something highly unlikely to collide with
other APIs. Make sure you provide proper .valueOf() 224 and
.toString() 225 methods to convert back to the original primitive
types.
Consistency
Jake Archibald226 once had a slide defining Consistency. It simply read
Not PHP227. Do. Not. Ever. Find yourself naming functions like
str_repeat(), strpos(), substr(). Also, don’t ever shuffle around positions of
arguments. If you declared find_in_array(haystack, needle) at
some point, introducing findInString(needle, haystack) will invite
an angry mob of zombies to rise from their graves to hunt you down
and force you to write delphi for the rest of your life!
NAMING THINGS
I’ve been to numerous talks and sessions trying to teach me the finer
points of naming things. I haven’t left any of them without having
heard the above said quote, nor having learnt how to actually name
things. My advice boils down to keep it short but descriptive and go with
your gut. But most of all, keep it consistent.
The DateInterval example above introduced a method called un-
til() . We could have named that function interval() . The latter
would have been closer to the returned value, while the former is more
humanly readable. Find a line of wording you like and stick with it. Con-
sistency is 90% of what matters. Choose one style and keep that
224. https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/
valueOf
225. https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/
toString
226. https://twitter.com/jaffathecake
227. http://www.slideshare.net/slideshow/embed_code/5426258?startSlide=59
123
DESIGNING BETTER JAVASCRIPT APIS
Handling Arguments
How your methods accept data is more important than making them
chainable. While method chaining is a pretty generic thing that you can
easily make your code do, handling arguments is not. You’ll need to
think about how the methods you provide are most likely going to be
used. Is code that uses your API likely to repeat certain function calls?
Why are these calls repeated? How could your API help the developer to
reduce the noise of repeating method calls?
jQuery’s css() 228 method can set styles on a DOM element:
jQuery("#some-selector")
.css("background", "red")
.css("color", "white")
.css("font-weight", "bold")
.css("padding", 10);
jQuery("#some-selector").css({
"background" : "red",
"color" : "white",
"font-weight" : "bold",
228. http://api.jquery.com/css/
124
"padding" : 10
});
jQuery’s on() 229 method can register event handlers. Like css() it ac-
cepts a map of events, but takes things even further by allowing a sin-
gle handler to be registered for multiple events:
You can offer the above function signatures by using the following
method pattern:
if (jQuery.isPlainObject(name)) {
// setting a map
map = name;
} else if (value !== undefined) {
// setting a value (on possibly multiple names),
convert to map
keys = name.split(" ");
map = {};
for (var i = 0, length = keys.length; i < length;
i++) {
map[keys[i]] = value;
}
} else if (name === undefined) {
// getting all values
return this.values;
229. http://api.jquery.com/on/
125
DESIGNING BETTER JAVASCRIPT APIS
} else {
// getting specific value
return this.values[name];
}
return this;
};
If you are working with collections, think about what you can do to re-
duce the number of loops an API user would probably have to make.
Say we had a number of <input> elements for which we want to set
the default value:
jQuery("input").each(function() {
var $this = jQuery(this);
$this.val($this.data("default"));
});
What if we could bypass that method with a simple callback that gets
applied to each <input> in the collection? jQuery developers have
thought of that and allow us to write less™:
jQuery("input").val(function() {
return jQuery(this).data("default");
});
It’s the little things like accepting maps, callbacks or serialized attribute
names, that make using your API not only cleaner, but more comfort-
able and efficient to use. Obviously not all of your APIs’ methods will
benefit from this method pattern—it’s up to you to decide where all this
makes sense and where it is just a waste of time. Try to be as consistent
about this as humanly possible. Reduce the need for boilerplate code with the
tricks shown above and people will invite you over for a drink.
126
HANDLING TYPES
Whenever you define a function that will accept arguments, you decide
what data that function accepts. A function to calculate the number of
days between two dates could look like:
By adding these six lines we’ve given the function the power to accept a
Date object, a numeric timestamp, or even a string representation like
Sat Sep 08 2012 15:34:35 GMT+0200 (CEST) . We do not know how and
for what people are going to use our code, but with a little foresight, we
can make sure there is little pain with integrating our code.
The experienced developer can spot another problem in the example
code. We’re assuming start comes before end . If the API user acciden-
tally swapped the dates, he’d be given a negative value for the number
of days between start and end . Stop and think about these situations
carefully. If you’ve come to the conclusion that a negative value doesn’t
make sense, fix it:
127
DESIGNING BETTER JAVASCRIPT APIS
return Math.abs(Math.floor((end.getTime() -
start.getTime()) / 86400000));
};
I’m not advocating you to do this everywhere and at all times. But these
innocent looking lines may save time and some suffering while inte-
grating your code.
230. https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Func-
tions_and_function_scope/arguments
128
console.log("but was actually passed in");
}
}
testUndefined("foo");
// prints: someArgument was undefined
testUndefined("foo", undefined);
// prints: someArgument was undefined, but was actually
passed in
NAMED ARGUMENTS
event.initMouseEvent(
"click", true, true, window,
123, 101, 202, 101, 202,
true, false, false, false,
1, null);
HOW OTHERS DO IT
Looking beyond our beloved language, we find Python knowing a con-
cept called named arguments232. It allows you to declare a function pro-
viding default values for arguments, allowing your attributed names to
be stated in the calling context:
namesAreAwesome();
// prints: 1, 2
231. https://developer.mozilla.org/en-US/docs/DOM/event.initMouseEvent
232. http://www.diveintopython.net/power_of_introspection/optional_arguments.html
129
DESIGNING BETTER JAVASCRIPT APIS
namesAreAwesome(3, 4);
// prints: 3, 4
namesAreAwesome(foo=5, bar=6);
// prints: 5, 6
namesAreAwesome(bar=6);
// prints: 1, 6
event.initMouseEvent(
type="click",
canBubble=true,
cancelable=true,
view=window,
detail=123,
screenX=101,
screenY=202,
clientX=101,
clientY=202,
ctrlKey=true,
altKey=false,
shiftKey=false,
metaKey=false,
button=1,
relatedTarget=null);
ARGUMENT MAPS
JavaScript not being Python (and ES.next being light years away), we’re
left with fewer choices to overcome the obstacle of “argument forests”.
jQuery (and pretty much every other decent API out there) chose to
233. http://wiki.ecmascript.org/doku.php?id=harmony:parameter_default_values
234. http://wiki.ecmascript.org/doku.php?id=harmony:rest_parameters
130
work with the concept of “option objects”. The signature of
jQuery.ajax()235 provides a pretty good example. Instead of accepting
numerous arguments, we only accept an object:
function dream(options) {
options = options || {};
if (options.accepts === "text") {
// prepare for receiving plain text
}
}
Not only does this prevent insanely long function signatures, it also
makes calling the function more descriptive:
dream({
accepts: "text",
async: true,
cache: false
});
Also, we do not have to touch the function signature (adding a new ar-
gument) should we introduce a new feature in a later version.
235. http://api.jquery.com/jquery.ajax/
236. http://api.jquery.com/jQuery.extend/
237. http://underscorejs.org/#extend
238. http://api.prototypejs.org/language/Object/extend/
131
DESIGNING BETTER JAVASCRIPT APIS
var default_options = {
accepts: "text",
async: true,
beforeSend: null,
cache: false,
complete: null,
// …
};
function dream(options) {
var o = jQuery.extend({}, default_options, options ||
{});
console.log(o.accepts);
}
You’re earning bonus points for making the default values publicly ac-
cessible. With this, anyone can change accepts to “json” in a central
place, and thus avoid specifying that option over and over again. Note
that the example will always append || {} to the initial read of the op-
tion object. This allows you to call the function without an argument
given.
var foo = 1;
var bar = true;
if (foo) {
132
// yep, this will execute
}
if (bar) {
// yep, this will execute
}
We’re quite used to this automatic casting. We’re so used to it, that we
forget that although something is truthful, it may not be the boolean
truth. Some APIs are so flexible they are too smart for their own good.
Take a look at the signatures of jQuery.toggle()239:
It will take us some time decrypting why these behave entirely different:
var foo = 1;
var bar = true;
var $hello = jQuery(".hello");
var $world = jQuery(".world");
$hello.toggle(foo);
$world.toggle(bar);
239. http://api.jquery.com/toggle/
133
DESIGNING BETTER JAVASCRIPT APIS
Extensibility
CALLBACKS
Callbacks can be used to achieve extensibility by configuration. You can
use callbacks to allow the API user to override certain parts of your
code. When you feel specific tasks may be handled differently than
your default code, refactor that code into a configurable callback func-
tion to allow an API user to easily override that:
var default_options = {
// ...
position: function($elem, $parent) {
$elem.css($parent.position());
}
};
function Widget(options) {
this.options = jQuery.extend({}, default_options,
134
options || {});
this.create();
};
Widget.prototype.create = function() {
this.$container =
$("<div></div>").appendTo(document.body);
this.$thingie =
$("<div></div>").appendTo(this.$container);
return this;
};
Widget.protoype.show = function() {
this.options.position(this.$thingie, this.$container);
this.$thingie.show();
return this;
};
Callbacks are also a generic way to allow API users to customize ele-
ments your code has created:
Widget.prototype.create = function() {
this.$container =
$("<div></div>").appendTo(document.body);
this.$thingie =
$("<div></div>").appendTo(this.$container);
135
DESIGNING BETTER JAVASCRIPT APIS
EVENTS
Events come naturally when working with the DOM. In larger applica-
tion we use events in various forms (e.g. PubSub) to enable communi-
cation between modules. Events are particularly useful and feel most
natural when dealing with UI widgets. Libraries like jQuery offer sim-
ple interfaces allowing you to easily conquer this domain.
Events interface best when there is something happening—hence
the name. Showing and hiding a widget could depend on circum-
stances outside of your scope. Updating the widget when it’s shown is
also a very common thing to do. Both can be achieved quite easily using
jQuery’s event interface, which even allows for the use of delegated
events:
Widget.prototype.show = function() {
var event = jQuery.Event("widget:show");
this.$container.trigger(event);
if (event.isDefaultPrevented()) {
// event handler prevents us from showing
return this;
}
this.options.position(this.$thingie, this.$container);
this.$thingie.show();
136
return this;
};
You can freely choose event names. Avoid using native events240 for
proprietary things and consider namespacing your events. jQuery UI’s
event names are comprised of the widget’s name and the event name
dialogshow . I find that hard to read and often default to dialog:show ,
mainly because it is immediately clear that this is a custom event,
rather than something some browser might have secretly implement-
ed.
Hooks
Traditional getter and setter methods can especially benefit from
hooks. Hooks usually differ from callbacks in their number and how
they’re registered. Where callbacks are usually used on an instance lev-
el for a specific task, hooks are usually used on a global level to cus-
tomize values or dispatch custom actions. To illustrate how hooks can
be used, we’ll take a peek at jQuery’s cssHooks241:
240. https://developer.mozilla.org/en-US/docs/DOM/DOM_event_reference
241. http://api.jquery.com/jQuery.cssHooks/
137
DESIGNING BETTER JAVASCRIPT APIS
? "circle"
: "box";
},
set: function(elem, value) {
elem.style.borderRadius = value == "circle"
? "50%"
: "0";
}
};
DateInterval.nameHooks = {
"yesterday" : function() {
var d = new Date();
d.setTime(d.getTime() - 86400000);
d.setHours(0);
d.setMinutes(0);
d.setSeconds(0);
return d;
}
};
DateInterval.prototype.start = function(date) {
if (date === undefined) {
return new Date(this.startDate.getTime());
}
242. http://blog.rodneyrehm.de/archives/11-jQuery-Hooks.html
138
if (!(date instanceof Date)) {
date = new Date(date);
}
this.startDate.setTime(date.getTime());
return this;
};
Generating Accessors
Any API is likely to have multiple accessor methods (getters, setters, ex-
ecutors) doing similar work. Coming back to our DateInterval exam-
ple, we’re most likely providing start() and end() to allow manipula-
tion of intervals. A simple solution could look like:
DateInterval.prototype.start = function(date) {
if (date === undefined) {
return new Date(this.startDate.getTime());
}
139
DESIGNING BETTER JAVASCRIPT APIS
this.startDate.setTime(date.getTime());
return this;
};
DateInterval.prototype.end = function(date) {
if (date === undefined) {
return new Date(this.endDate.getTime());
}
this.endDate.setTime(date.getTime());
return this;
};
As you can see we have a lot of repeating code. A DRY (Don’t Repeat
Yourself) solution might use this generator pattern:
function generateAccessor(key) {
var value = key + "Date";
return function(date) {
if (date === undefined) {
return new Date(this[value].getTime());
}
this[value].setTime(date.getTime());
return this;
};
}
140
for (var key in accessors) {
DateInterval.prototype[key] = generateAccessor(key,
accessors[key]);
}
if (jQuery.isPlainObject(name)) {
// setting a map
map = name;
} else if (value !== undefined) {
// setting a value (on possibly multiple names),
convert to map
keys = name.split(" ");
map = {};
for (var i = 0, length = keys.length; i < length;
i++) {
map[keys[i]] = value;
}
} else {
return get.call(this, name);
}
141
DESIGNING BETTER JAVASCRIPT APIS
return this;
};
}
DateInterval.prototype.values = wrapFlexibleAccessor(
function(name) {
return name !== undefined
? this.values[name]
: this.values;
},
function(name, value) {
this.values[name] = value;
}
);
Digging into the art of writing DRY code is well beyond this chapter.
Rebecca Murphey243 wrote Patterns for DRY-er JavaScript244 and
Mathias Bynens’245 slide deck on how DRY impacts JavaScript perfor-
mance246 are a good start, if you’re new to the topic.
243. https://twitter.com/rmurphey
244. http://rmurphey.com/blog/2010/07/12/patterns-for-dry-er-javascript/
245. https://twitter.com/mathias
246. http://slideshare.net/mathiasbynens/how-dry-impacts-javascript-performance-faster-
javascript-execution-for-the-lazy-developer
142
// by value
function addOne(num) {
num = num + 1; // yes, num++; does the same
return num;
}
var x = 0;
var y = addOne(x);
// x === 0 <--
// y === 1
// by reference
function addOne(obj) {
obj.num = obj.num + 1;
return obj;
}
The by reference handling of objects can come back and bite you if you’re
not careful. Going back to the DateInterval example, check out this
bugger:
143
DESIGNING BETTER JAVASCRIPT APIS
If this summary did not suffice, read the excellent chapter By Value
Versus by Reference250 from O’Reilly’s JavaScript – The Definitive
Guide251.
jQuery('.wont-find-anything')
// executed although there is nothing to execute
against
.somePlugin().someOtherPlugin();
jQuery.fn.somePlugin = function() {
if (!this.length) {
// "abort" since we've got nothing to work with
return this;
}
247. http://api.jquery.com/jQuery.extend/
248. http://underscorejs.org/#extend
249. http://api.prototypejs.org/language/Object/extend/
250. http://docstore.mik.ua/orelly/webprog/jscript/ch11_02.htm
251. http://docstore.mik.ua/orelly/webprog/jscript/index.htm
144
for (var i = 10000; i > 0; i--) {
// I'm just wasting your precious CPU!
// If you call me often enough, I'll turn
// your laptop into a rock-melting jet engine
}
return this.each(function() {
// do the actual job
});
};
Handling Errors
145
DESIGNING BETTER JAVASCRIPT APIS
Errors like these are a major pain to debug. Don’t waste other people’s
time. Inform an API user if he did something stupid:
if (Object.prototype.toString.call(callback) !==
'[object Function]') { // see note
throw new TypeError("callback is not a function!");
}
252. http://api.jquery.com/jQuery.isfunction/
253. http://underscorejs.org/#isFunction
146
Think of input validation this way: A couple of lines of code behind
your API can make sure that no developer has to spend hours chasing
down weird bugs because they accidentally gave your code a string in-
stead of a number. This is the one time you can tell people they’re wrong
and they’ll actually love you for doing so.
Going Asynchronous
So far we’ve only looked at synchronous APIs. Asynchronous methods
usually accept a callback function to inform the outside world, once a
certain task is finished. This doesn’t fit too nicely into our fluent inter-
face scheme, though:
Api.protoype.async = function(callback) {
console.log("async()");
// do something asynchronous
window.setTimeout(callback, 500);
return this;
};
Api.protoype.method = function() {
console.log("method()");
return this;
};
// running things
api.async(function() {
console.log('callback()');
}).method();
DEFERREDS (PROMISES)
To some extent we can counter the mess that is a mix of asynchronous
and synchronous API calls with Promises254. jQuery knows them as De-
ferreds255. A Deferred is returned in place of your regular this , which
forces you to eject from method chaining. This may seem odd at first,
147
DESIGNING BETTER JAVASCRIPT APIS
Api.protoype.async = function() {
var deferred = $.Deferred();
console.log("async()");
window.setTimeout(function() {
// do something asynchronous
deferred.resolve("some-data");
}, 500);
return deferred.promise();
};
api.async().done(function(data) {
console.log("callback()");
api.method();
});
254. http://wiki.commonjs.org/wiki/Promises/A
255. http://api.jquery.com/category/deferred-object/
256. http://sitr.us/2012/07/31/promise-pipelines-in-javascript.html
148
foobar.bar()
.baz()
.bam()
.someError();
This technique does have its benefits (though better debugging is not a
solid part of it). Code that is written like the above example is even sim-
pler to read. Line-based differentials (used in version control systems
like SVN, GIT) might see a slight win as well. Debugging-wise, it is only
Chrome (at the moment), that will show someError() to be on line
four, while other browsers treat it as line one.
Adding a simple method to logging your objects can already help a
lot—although that is considered “manual debugging” and may be
frowned upon by people used to “real” debuggers:
DateInterval.prototype.explain = function() {
// log the current state to the console
console.dir(this);
};
FUNCTION NAMES
Throughout this chapter you’ve seen a lot of demo code in the style of
Foo.prototype.something = function(){} . This style was chosen to
keep examples brief. When writing APIs you might want to consider ei-
ther of the following approaches, to have your console properly identify
function names:
Foo.prototype.something = function() {
// yadda yadda
};
Foo.prototype.something.displayName = "Foo.something";
149
DESIGNING BETTER JAVASCRIPT APIS
Documenting APIs
One of the hardest tasks of software development is documenting
things. Practically everyone hates doing it, yet everybody laments
about bad or missing documentation of the tools they need to use.
There is a wide range of tools that supposedly help and automate docu-
menting your code:
257. http://kangax.github.com/nfe/
258. https://twitter.com/kangax
259. http://yui.github.com/yuidoc/
260. https://github.com/p120ph37/node-jsdoc-toolkit
261. https://github.com/cbou/markdox
262. https://github.com/visionmedia/dox
263. http://jashkenas.github.com/docco/
264. https://github.com/senchalabs/jsduck
265. https://github.com/jsdoc3/jsdoc
266. http://en.wikipedia.org/wiki/DocBook
150
1. Function signatures aren’t the only documentation you need, but most
tools focus only on them.
3. API docs usually fail horribly at explaining things behind the scenes (flow,
events, etc).
If you can’t (or don’t) want to adjust your code to fit one of the listed
documentation tools, projects like Document-Bootstrap267 might save
you some time setting up your home brew documentation.
Make sure your Documentation is more than just some generated
API doc. Your users will appreciate any examples you provide. Tell
them how your software flows and which events are involved when do-
ing something. Draw them a map, if it helps their understanding of
whatever it is your software is doing. And above all: keep your docs in
sync with your code!
SELF-EXPLANATORY CODE
Providing good documentation will not keep developers from actually
reading your code—your code is a piece of documentation itself. When-
ever the documentation doesn’t suffice (and every documentation has
its limits), developers fall back to reading the actual source to get their
questions answered. Actually, you are one of them as well. You are most
likely reading your own code again and again, with weeks, months or
even years in between.
You should be writing code that explains itself. Most of the time this
is a non-issue, as it only involves you thinking harder about naming
things (functions, variables, etc) and sticking to a core concept. If you
find yourself writing code comments to document how your code does
something, you’re most likely wasting time—your time, and the read-
er’s as well. Comment on your code to explain why you solved the prob-
lem this particular way, rather than explaining how you solved the
problem. The how should become apparent through your code, so don’t
267. http://gregfranko.com/Document-Bootstrap/
151
DESIGNING BETTER JAVASCRIPT APIS
Conclusion
• An API is a contract between you (the provider) and the user (the con-
sumer). Don’t just change things between versions.
• You should invest as much time into the question How will people use my
software? as you have put into How does my software work internally?
• With a couple of simple tricks you can greatly reduce the developer’s
efforts (in terms of the lines of code).
• Good APIs are flexible, better APIs don’t let you make mistakes.
268. http://vimeo.com/35689836
269. http://www.slideshare.net/jaffathecake/reusable-code-for-good-or-for-awesome
270. https://twitter.com/jaffathecake
271. http://www.youtube.com/watch?v=heh4OeB9A-c
272. http://addyosmani.com/resources/essentialjsdesignpatterns/book/
273. https://twitter.com/addyosmani
274. https://twitter.com/bassistance
275. https://twitter.com/addyosmani
276. https://twitter.com/hellokahlil
152
About The Authors
Addy Osmani
Addy Osmani is a Developer Programs Engineer on the Chrome team at
Google. A passionate JavaScript developer, he has written open-source
books like ’Learning JavaScript Design Patterns’ and ’Developing Back-
bone Applications’, having also contributed to open-source projects like
Modernizr and jQuery. He is currently working on ’Yeoman’ - an opin-
ionated workflow for building beautiful applications.
Christian Heilmann
Christian Heilmann is an international Developer Evangelist working
for Mozilla in the lovely town of London, England.
Jörn Zaefferer
Jörn is a freelance web developer, consultant and trainer, residing in
Cologne, Germany. Jörn evolved jQuery’s testsuite into QUnit, a
JavaScript unit testing framework, and maintains it. He created and
maintains a number of popular plugins. As a jQuery UI development
lead, he focuses on the development of new plugins, widgets and utili-
ties.
Philip Tellis
Philip Tellis is a geek, speedfreak and co-founder of lognormal.com277
where he tries to infer various characteristics of web users’ networks.
When he isn’t at his computer, he reads, cooks, bikes or falls off his
skateboard.
Rodney Rehm
Rod is a Web Developer based in Lottstetten, Germany. He is mainly
developing in PHP and JavaScript. He’s quite decent with databases and
distributed systems for scalability and redundancy. Created URI.js,
jQuery Context Menu, Apple’s PList in PHP and works on Smarty - PHP
Template Engine.
277. http://lognormal.com/
153
ABOUT THE AUTHORS
Zack Grossbart
Zack Grossbart is an engineer278, designer, and author279. He’s a found-
ing member of the Spiffy UI280 project, the architect of the WordPress
Editorial Calendar281, and an Architecting Engineer and Human Factors
Specialist with NetIQ282. Zack began loading DOS from a floppy disk
when he was five years old. He first worked professionally with com-
puters when he was 15 and started his first software company when he
was 16. Zack lives in Cambridge, Massachusetts with his wife and
daughter.
278. http://www.zackgrossbart.com/hackito/
279. http://www.zackgrossbart.com/blog/toc/
280. http://www.spiffyui.org/
281. http://stresslimitdesign.com/editorial-calendar-plugin
282. http://www.netiq.com/
154
About Smashing Magazine
Smashing Magazine283 is an online magazine dedicated to Web design-
ers and developers worldwide. Its rigorous quality control and thor-
ough editorial work has gathered a devoted community exceeding half
a million subscribers, followers and fans. Each and every published arti-
cle is carefully prepared, edited, reviewed and curated according to the
high quality standards set in Smashing Magazine’s own publishing pol-
icy284.
Smashing Magazine publishes articles on a daily basis with topics
ranging from business, visual design, typography, front-end as well as
back-end development, all the way to usability and user experience de-
sign. The magazine is—and always has been—a professional and inde-
pendent online publication neither controlled nor influenced by any
third parties, delivering content in the best interest of its readers. These
guidelines are continually revised and updated to assure that the quali-
ty of the published content is never compromised.
283. http://www.smashingmagazine.com
284. http://www.smashingmagazine.com/publishing-policy/
285. http://www.smashing-media.com
155