JavaScript のスピードアップ (Google Tech Talk)

最近 Google Tech Talk の Speed Up Your JavaScript を見たので、 今後のためにメモしておく。

Nicholas C. Zakas さんがプレゼンテーションの中で次の4つの点について説明している。

  • スコープ管理
  • データアクセス
  • ループ
  • DOM

スコープ管理

  1. ローカル変数を使う (変数などはローカル変数に保存して使う)
  2. “with” ステートメントをさける。また “try-catch” を使う時は注意 (これらはスコープチェインの先頭にオブジェクトを追加する)

#1の例: ローカル変数を使用

function func1(){
    var doc = document;
    var header  = doc.getElementById('header');
    var content = doc.getElementById('content');
    // do something
}

データアクセス

  1. リテラルとローカル変数が一番早い (ローカル変数を使う)
  2. オブジェクトのプロパティの階層が深くなるほど時間がかかる

#1の例:

function process(data){
    var count = data.count;
    var item  = data.item;
    for (var i=0; i<count; i++){
        processData(item[i]);
    }
}

 

ループ

  1. for-each、 for-in を避ける
  2. 一回のイテレーションでの処理を減らす (これには条件式やインクリメントなども含む)
  3. イテレーションの回数を減らす

#2の例:

次のコードは

var len = values.length;
for (var i=len; i--){
    //do something
}

 

下のコードよりも早い

for (var i=0; i<value.length; i++){
    // do something
}

 

DOM

  1. DOM は document が変更された時に更新される (オブジェクトにアクセスする度にクエリが実行される)
  2. 長さやアイテムをローカル変数に保存する
  3. アイテムに頻繁にアクセスする場合は、ローカルの配列にコピーする
  4. ノードの追加、削除の回数を減らす (例えば document.fragment を使う)
  5. ‘style’ プロパティを変更しない (class を定義し、className を変更する)
  6. レイアウト情報へのアクセスを減らす (2回以上アクセスするなら、ローカル変数に保存する)

#1の例: 以下は無限ループ

var divs = document.getElementByTagName('div');
for (var i=0; i<divs.length; i++){
    var div = document.createElement('div');
    document.body.appendChild(div);
}

 

#3の例:

function array(items){
    try {
        return Array.prototype.concat.call(items);
    } catch (ex) {
        var i      = 0,
            len    = items.length,
            result = Array(len);
        while (i<len){
            result[i] = items[i];
            i++;
        }
        return result;
    }
}

 

#4の例:

var list = document.getElementById('list');
var fragment = document.createDocumentFlagment();
for (var i=0; i<10; i++){
    var item = document.createElement('li');
    item.innerHTML = 'Option #' + (i+1);
    fragment.appendChild(item);
}
list.appendChild(fragment);

上記は将来的にブラウザのJSエンジンが改善された場合、不要になるかもしれないが、現時点で有効なテクニック。

その他、参考になるページ (Nicholas C. Zakas’s blog:

JavaScript のスピードアップ (Google Tech Talk)

Speed up your JavaScript (Google Tech Talk)

I watched Google Tech Talk Video – Speed Up Your JavaScript yesterday. I take a note of some points of the technic here for my study.

Nicholas C. Zakas showed 4 points for speed up in the presentation.

  • Scope Management
  • Data access
  • Loops
  • DOM

Scope Management

  1. Use Local Valiables. (Store a valiable to a local valiable.)
  2. Avoid “with” statement. Be careful “try-catch”. (Both add an object to scope chain)

Example of #1: Using local valiable

function func1(){
    var doc = document;
    var header  = doc.getElementById('header');
    var content = doc.getElementById('content');
    // do something
}

Data access

  1. Literal & Local Valiables are fastest (use local valiables)
  2. Deeper object property is slower

Example of #1:

function process(data){
    var count = data.count;
    var item  = data.item;
    for (var i=0; i<count; i++){
        processData(item[i]);
    }
}

Loops

  1. Avoid for-each, for-in
  2. Less work per iteration (includes condition evaluation and incrementing/decrementing)
  3. Less number of iteration

Example of #2:

The following code is faster

var len = values.length;
for (var i=len; i--){
    //do something
}

than below.

for (var i=0; i<value.length; i++){
    // do something
}

DOM

  1. DOM is updated when document is changed (the query re-runs when the object is accessed)
  2. Store length, items in local valiables
  3. Copy items in local (regular) array if it’s frequently used
  4. Less node add/remove (use document.fragment, for example)
  5. Avoid changing ‘style’ property (define class and change className instead)
  6. Minimize access to layout information (store it in local valiables if it’s accessed more than once)

Example of #1: The following loop is infiite

var divs = document.getElementByTagName('div');
for (var i=0; i<divs.length; i++){
    var div = document.createElement('div');
    document.body.appendChild(div);
}

Example of #3:

function array(items){
    try {
        return Array.prototype.concat.call(items);
    } catch (ex) {
        var i      = 0,
            len    = items.length,
            result = Array(len);
        while (i<len){
            result[i] = items[i];
            i++;
        }
        return result;
    }
}

Example of #4:

var list = document.getElementById('list');
var fragment = document.createDocumentFlagment();
for (var i=0; i<10; i++){
    var item = document.createElement('li');
    item.innerHTML = 'Option #' + (i+1);
    fragment.appendChild(item);
}
list.appendChild(fragment);

These technics may not be effective in future for browsers that have more optimising JS engine, but it’s effective for now.

Other references (on Nicholas C. Zakas’s blog:

Speed up your JavaScript (Google Tech Talk)

実現すべきなのは、ひとりが使い始めたら、周りはまったく何もしなくても、当人とその友だち全員の役に立つ仕掛けだ。

jwz: グループウェア、ダメ! ‐ Groupware Bad

(original sentence)

The trick you want to accomplish is that when one person is using your software, it suddenly provides value to that person and their entire circle of friends, without the friends having had to do anything at all.

Quote