JavaScript: Better and Faster

Ben Cherry

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 that

modules

how to make BC.utilnot 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

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

thanks for coming!


posted @ 2011-05-10 15:04  郭培  阅读(394)  评论(0编辑  收藏  举报