by shigemk2

当面は技術的なことしか書かない

WebDBPress Vol.58 JavaScriptの玉手箱 その2

関数呼び出しのコスト

速度は、4> 3 > 1 = 2 > 5の順になる。

  1. 名前付きのローカル関数を呼び出す
  2. 匿名のローカル関数を呼び出す
  3. 上位スコープにある関数を呼び出す
  4. グローバル関数を window を省略し呼び出す
  5. グローバル関数を window["..."] で呼び出す
// Function/call cost.htm
// グローバル関数
function globalFunction(i) {
  return i + i;
}
window.onload = function() { 
  function _localNamed(i) {
    // 名前付きのローカル関数 
    function localNamedFunction(i) {
      return i + i; 
    }
    // 1 名前付きのローカル関数を呼び出す
    return localNamedFunction(i);
  }

  function _localAnon(i) {
  // 匿名のローカル関数
  var localAnonymouseFunction = function(i) {
      return i + i; 
    };
    // 2 匿名のローカル関数を呼び出す
    return localAnonymouseFunction(i); 
  }

  function _scope(i) {
    // 3 上位スコープにある関数を呼び出す 
    return scopeFunction(i);
  }
  // 上位スコープにある関数 
  function scopeFunction(i) {
    return i + i; 
  }

  function _global(i) {
    // 4 グローバル関数をwindowを省略し呼び出す 
    return globalFunction(i);
  }

  function _windowGlobal(i) {
    // 5 グローバル関数をwindow["..."]で呼び出す
    return window["globalFunction"](i);
  }
};

結果の受けとりかたによるコストの違い

  • return で受け取る
  • 引数でオブジェクトを渡し、参照で受け取る
  • スコープ変数を経由する
  • クロージャを経由する

戻り値がリテラル値の場合は普通にreturnで値を返すようにする。

eval と Function コンストラクタ

特定の条件を満たした場合に、
(与える expression が静的で周囲の最適化を阻害せず外部変数 を参照しないケース)
eval は Function の数倍高速に動作するようだ。

evalに与えるexpressionが動的なケースでは、
Function よりも遅くなる傾向がある(実際には ブラウザの実装に依存する)。

try~catch を控える

例外オブジェクトの生成は非常にコスト高なので極力使わない設計を考える。

ループ処理内で try~catch していたコード を
関数の外側で try~catch するようにしてもあまり効果はない。

また、

ほかのブラウザでもそれなりに 遅いため、nodeList[i] や nodeList.length への
アクセスは一度きりにする。

  • nodeList.lengthに限らず、ループ中に何度もlength を取得しない
  • ポストインクリメント演算子(i++)は遅いため、プレインクリメント演算子(++i)にする
  • 毎回 document.getElementById でノードを検索せずに、処理開始前に一度だけにする
  • document や window などの多用するオブジェクトは変数に格納しておく
  • var を場当たり的に使用せずスコープの先頭で一度だけ使用する。varが乱雑にあると minify の圧縮率も悪くなる
  • Hash を Array で代用

key と value のどちらか一方に意味があるなら、 HashではなくArrayを使い、
for inループをforループに書き換えると速くなる

  • Array#forEach を for に書き換える

毎回ローカルスコープを使うから遅い

(phpと同様、==はキャストするから遅くなりがち)


型をまとめて判別する

  1. ===演算子でnull, undefined, windowを判別する
  2. nodeType プロパティの値で document と Nodeオブジェクトを判別する
  3. 必要に応じて instanceof 演算子でユーザ定義型を判別する
  4. typeof演算子で、リテラル値の型(Boolean、Number、NaN、Infinity、String)を判別する
  5. Object.prototype.toString.call( ) で、RegExp、 Array、Boolean、Number、String、Function、Date、Error オブジェクトを判別する
  6. length プロパティ FakeArray(NodeList やArguments)オブジェクトを判別する
  7. 最後まで残ったものがObject