JavaScript: Better and Faster
Performance Engineer at Slide, Inc.
1.The JavaScript Language
just what you need to know to understand this talk
variables and functions
var foo = 1; // variable statement
function bar() {} // function statement
(function baz() {}); // function expression
(function (spam) {}(1)); // function parameter
those are the only ways you should declare things
scope
var foo = 1;
(function () {
var baz = 2;
foo = 3;
}());
alert(foo); // 3
alert(baz); // ReferenceError
JavaScript has function-level scope
hoisting
var foo = 1;
(function () {
alert(foo); // undefined
var foo = 2;
}());
alert(foo); // 1
names are always hoisted to the top of their scope , and always initialized to undefined
closure
var foo = (function () {
var bar = 5;
return function () {
alert(bar);
};
}());
foo(); // 5
setTimeout(foo, 10000); // 5, 10s later
functions have access to their original scope chain
2.Run-Time Performance common pitfalls
"namespaces"
Python
import bc.util
bc.util.some_function()
//# this is quite fast
JavaScript
BC.util.someFunction();
//but this is very slow
here's what happens
BC.util.someFunction();
// lookup BC
// resolve property util
// resolve property someFunction
// execute function
imagine that in a loop!
store that reference
var some_function = BC.util.some_function,i;
for (i = 0; i < 1000000; i += 1) {
some_function();
}
this is much faster
scope chains
the slow way
var foo = 1;
function bar() {
var i;
for (i = 0; i < 10000; i += 1) {
alert(foo);
}
}
bar();
foo
is one step down the chain
faster
var foo = 1;
function bar() {
var myFoo = foo, i;
for (i = 0; i < 10000; i += 1) {
alert(myFoo);
}
}
bar();
now myFoo
is on the end of the chain
arrays
arrays
var a = [];
typeof a; // "object"
a.length; // 0
a[10] = 1;
a.length; // 11
a["something_else"] = 1;
a.something_else; // 1
a.length; // 11
this means arrays are slower than you expect , but some browsers do optimize them
loops
for loops
// slow method
var i;
for (i = 0; i < someArray.length; i += 1) {
// ...
}
// faster
var i, l;
for (i = 0, l = someArray.length; i < l; i += 1) {
// ...
}
for-in loops
var key;
for (key in someObject) {
// ...
}
these are slow, avoid them
var keys = ["foo", "bar", "baz"], i, l, key;
for (i = 0, l = keys.length; i < l; i += 1) {
key = keys[key];
// ... someObject[key] ...
}
this is much faster, and is preferred
arguments
function foo() {
return arguments[0];
}
foo(1); // 1
much slower (up to 100x) than using parameters , arguments
should be avoided if possible
3.Inheritance
don't use it , unless you have to , and that's all I have to say about thatmodules
how to make BC.util
? not the same as inheritance
BC.util = (function (BC) {
var util = {},foo; // private variable
// public
util.someFunction = function(){};
// private
function someOther(){}
return util;
}(BC));
this a fast, reusable pattern
4.The DOM
it's absolutely terrible , but I have three simple rules to make it better
rule 1:never edit the live tree
you can detach sub-trees
var elem = $("#myelem"),
parent = elem.parent();
elem.detach();
// ... muck with elem and sub-elements ...
parent.append(elem);
.detach()
is new in jQuery 1.4
or you can clone and replace
var old = $("#myelem"),
clone = old.clone();
// ... muck with the clone ...
old.replaceWith(clone);
but be careful about event handlers , use .clone(true)
to preserve them
rule 2:build bottom-up
bottom-up construction
var child = document.createElement("div"),
parent = document.createElement("div"),
grand = document.createElement("div");
parent.appendChild(child);
grand.appendChild(parent);
document.body.appendChild(grand);
rule 3:minimize event handlers
memory leaks in IE
$(".myelems").bind("click", function () {/* ... */});
// ...
$(".myelems").remove();
// aww snap, memory leak!
was that really that surprising?
unbind before removal
$(".myelems").unbind("click").remove();
// phew!
but that kind of sucks too
use event delegation
$(".myelems").live("click", function () {/* ... */});
$(".myelems").remove();
$("<div/>").addClass("myelems").appendTo($("body"))
the new <div>
gets the handler for free
avoid handlers in loops
function makeElem(id) {
return $("<div/>").attr("id", id).click(function () {
alert($(this).attr("id"));
});
}
var i;
for (i = 0; i < 1000; i += 1) {
someParent.append(makeElem(i));
}
this is slow!
faster
function handler() {
alert(this.attr("id"));
}
function makeElem(id) {
return $("<div/>").attr("id", i).click(handler);
}
var i;
for (i = 0; i < 1000; i += 1) {
someParent.append(makeElem(i));
}
5.long-running operations
sometimes you can't avoid it
problem script
var i;
for (i = 0; i < 10000; i += 1) {
oneMillisecondOperation(i);
}
browser locks for 10s!
solution: timers
var i = 0;
setTimeout(function iterate() {
var stop = i + 500;
for (; i < stop; i += 1) {
oneMillisecondOperation(i);
}
setTimeout(iterate, 20);
}, 0);
split into half-second chunks, with 20ms in between
minimum intervals
setTimeout(function () {
alert("foo");
}, 0);
// how long until "foo"?
no browser really does 0ms ; Chrome is ~5ms, but IE is ~18ms ;others are ~10-12ms
6.page-load performance
getting your script running quickly
<html>
<head></head>
<body>
<!-- all scripts at the bottom of <body>-->
<script></script>
</body>
</html>
always at the bottom, so the page is not blocked
avoid inline scripts
<script>
function foo() {
// ...
}
</script>
these cannot be cached or minified , only put dynamic values here
minification
function foo() {
var bar = 1;
return bar + 5;
}
function foo(){var a=1;return a+5}
our build process does this for you , make sure you're using it!
7.Performance Tools
my JavaScript profiler
var profiler = performance.newProfiler();
function foo() {
profiler.begin("body");
// ... some operations ...
profiler.end("body");
}
// ... repeated calls to foo() ...
profiler.report(); // alerts time spent in "body
source at http://gist.github.com/322060
other tools
- JSLint (JavaScript syntax checker) http://jslint.com/
- YSlow (Firebug extension) http://developer.yahoo.com/yslow/
- Page Speed (Firebug extension) http://code.google.com/speed/page-speed/
- Speed Tracer (Chrome extension) http://code.google.com/webtoolkit/speedtracer/
- dynaTrace AJAX (Windows program/IE plugin) http://ajax.dynatrace.com/pages/
8.random extras
somewhat important
parseInt
needs radix!
parseInt("123"); // 123
parseInt("10"); // 10
parseInt("010"); // 8 -> WTF?
// with a radix
parseInt("010", 10); // 10 -> crisis averted!
never forget your radix
sort
has issues
var a = [3,1,2];
a.sort(); // [1,2,3]
a = [10,1,2];
a.sort(); // [1,10,2] - f**ing javascript!
a.sort(betterComparison); // [1,2,10]
.sort()
sorts alphabetically , write your own comparison function
CSS expressions are evaluated right-to-left!
#foo div a {/* ... */}
this starts by looking at every <a>
then it looks for those whose parent is <div>
then it checks to see if its parent is #foo
Useful Resources
because this was not nearly exhaustive
dead trees
- High Performance Web Sites, by Steve Souders (Yahoo!, at the time)
- Even Faster Web Sites, by Steve Souders (Google, now)
- JavaScript: The Good Parts, by Douglas Crockford (Yahoo!)
- High Performance JavaScript, by Nicholas Zakas (Yahoo!) (coming soon)
- Secrets of the JavaScript Ninja, by John Resig (Mozilla/jQuery) (coming soon)
blogs
this slideshow
was running in the browser, with JavaScript