Hatena::Groupjavascript

JavaScriptで遊ぶよ

 | 

2009-01-15

グローバルな変数・関数へのアクセス

02:23

グローバルなスコープに変数を作りたい誘惑はある. なにしろ楽だから.

けれど, それはスクリプトを遅くする原因になる.


まず, 関数その他のスコープからそのグローバル変数を参照しようとすると,

スクリプトエンジンはグローバル変数がみつかるまでスコープを順に上らないといけない.

ローカルのスコープにある変数はもっとすぐみつかる.


グローバルのスコープにある変数はスクリプトの寿命と同じ間だけ残る.

ローカルのスコープでは, そのスコープが消えた時に変数は破棄される.

使っていたメモリはガベージ・コレクタで解放される.


最後に, グローバルのスコープは window オブジェクトに共有される.

つまり二つのスコープにまたがる. 一つだけではなくなってしまう.

グローバルスコープの変数は常に名前で参照される.

事前計算で最適化されたインデックスではない.

ローカルスコープにはインデックスが使われる.

このように, スクリプトエンジンがグローバル変数をみつけるのには時間がかかる.


関数もふつうグローバルスコープに作られる.

つまり関数が他の関数を, その関数が更に他の関数を呼ぶと,

スクリプトエンジンがグローバルスコープまで戻って関数をみつける回数は増えていく.

EfficientJavaScript - Dev.Opera - 効率的な JavaScript

グローバルな変数は、

  • スコープを上って探す
  • メモリを食う
  • 名前で参照

するので、遅いから使わないほうがいい。

これはいいとして、最後の太字。

  • 関数は普通「ローカル」というのが無くグローバルスコープに作られるため、グローバルスコープで宣言しようがローカルで宣言しようがアクセスの差はない (?)

と読めるんだけど、そういうことはないような気がする。

(window.document へのアクセスが遅いから var doc=document をローカルに作ってそれを参照してあげましょう、ってやつと同じことじゃないかな?)

テスト1

グローバル変数へのアクセス。

javascript: 
var a; 
(function() {
  var t = new Date().getTime();
  for (var i = 0; i < 1000000; i++) a;
  alert(new Date().getTime() - t)
})();

134ms

ローカルにエイリアスを作る。

javascript: 
var a; 
(function() {
  var t = new Date().getTime();
  var alias = a;
  for (var i = 0; i < 1000000; i++) alias;
  alert(new Date().getTime() - t)
})();

60ms

ローカルで同じオブジェクトを宣言しても (ほとんど) 同じぐらいのアクセススピードになるっぽい。

ちなみに数字は何回か実行した平均を目分量で書いているので、あまり厳密なテストではない。

テスト2

グローバル関数へのアクセス。

javascript: 
function testFunc() {};
(function() {
  var t = new Date().getTime();
  for (var i = 0; i < 1000000; i++) testFunc();
  alert(new Date().getTime() - t)
})();

380ms

ローカルにグローバル関数のエイリアスを作る。

javascript: 
function testFunc() {} 
(function() {
  var t = new Date().getTime();
  var aliasFunc = testFunc;
  for (var i = 0; i < 1000000; i++) aliasFunc();
  alert(new Date().getTime() - t)
})();

280ms

ちなみに、ローカル関数にアクセス。

javascript: 
(function() {
  function testFunc() {};
  var t = new Date().getTime();
  for (var i = 0; i < 1000000; i++) testFunc();
  alert(new Date().getTime() - t)
})();

280ms

同じぐらい。ただの参照だったら当然か。

テスト3

グローバル関数がグローバル変数を参照していたらどうなるか。

javascript: 
var obj;
function testFunc() {
  obj
} 
(function() {
  var t = new Date().getTime();
  for (var i = 0; i < 1000000; i++) testFunc();
  alert(new Date().getTime() - t)
})();

440ms

関数のエイリアスにアクセス。

javascript: 
var obj = {};
function testFunc() {
  obj
} 
(function() {
  var t = new Date().getTime();
  var aliasFunc = testFunc;
  for (var i = 0; i < 1000000; i++) aliasFunc();
  alert(new Date().getTime() - t)
})();

360ms

当然ちょっと速くなる。

最後のケースで、testFunc をまったく弄らずに testFunc からグローバルオブジェクトへのアクセスを速くする方法はないかなあ。

トラックバック - http://javascript.g.hatena.ne.jp/edvakf/20090115
 |