JavaScript コードの最適化 (Optimizing JavaScript code)
JavaScrip の実行効率を上げる方法のいくつかの例が "Let’s make the web faster – Google Code" (日本語訳:JavaScriptの最適化について、code.google.comの記事の適当訳 – それ図解で。・・・tohokuaikiのチラシの裏)に掲載されていた。
文字列の結合
+
を使った文字列結合よりも、配列要素にして join
を使った方が効率的(IE6,7 でのガベージコレクションに影響する)。
速度(Firefox3.5の場合)は join
よりも +
を使った方が速かった。
$(function() { $('#run').click(function() { console.profile; addStr(); joinStr(); console.profileEnd(); }); }); var fibonacci = function(n) { if (n === 0) { return n; } else { return fibonacci(n - 1); } } var addStr = function() { var fibonacciStr = 'First 20 Fibonacci Numbers'; for (var i = 0; i < 20; i++) { fibonacciStr += i + ' = ' + fibonacci(i) + ''; } } var joinStr = function() { var strBuilder = ['First 20 fibonacci numbers:']; for (var i = 0; i < 20; i++) { strBuilder.push(i, ' = ', fibonacci(i)); } var fibonacciStr = strBuilder.join(''); }
クラスメソッドの定義
prototype
を使うと、生成するインスタンスの数に関係なく1つのプロトタイプ関数が作られるだけで済む。
インスタンス毎にクロージャが作られることもない。
var baz = {}; baz.Bar = function() { this.foo = function() { return 'Hello from BAZ.'; }; }; var moo = {}; moo.Bar = function() { }; moo.Bar.prototype.foo = function() { return 'Hello from MOO.'; }; $(function() { $('#run').click(function() { var b = new baz.Bar(); var m = new moo.Bar(); $('#debug').html( "b.toSource() => " + b.toSource() + "\n" + "m.toSource() => " + m.toSource() + "\n" + "b.foo() => " + b.foo() + "\n" + "m.foo() => " + m.foo() ); }); });
実行結果
b.toSource() => ({foo:(function () {return "Hello from BAZ.";})}) m.toSource() => ({}) b.foo() => Hello from BAZ. m.foo() => Hello from MOO.
インスタンス変数の初期化
prototype
でインスタンス変数を宣言・初期化すると、コンストラクタが呼ばれる度に初期化処理コードが実行されることを防ぐ。
var foo = {}; foo.Bar = function() { this.prop1_ = 4; this.prop2_ = true; this.prop3_ = []; this.prop4_ = 'blah'; }; var hoge = {}; hoge.Bar = function() { this.prop3_ = []; }; hoge.Bar.prototype.prop1_ = 4; hoge.Bar.prototype.prop2_ = true; hoge.Bar.prototype.prop4_ = 'blah'; $(function() { $('#run').click(function() { var f = new foo.Bar(); var h = new hoge.Bar(); $('#debug').html( "f.toSource() => " + f.toSource() + "\n" + "h.toSource() => " + h.toSource() + "\n" + "f.prop1 => " + f.prop1_ + "\n" + "h.prop1 => " + h.prop1_ + "\n" + "f.prop2 => " + f.prop2_ + "\n" + "h.prop2 => " + h.prop2_ + "\n" + "f.prop3 => " + f.prop3_.toSource() + "\n" + "h.prop3 => " + h.prop3_.toSource() + "\n" + "f.prop4 => " + f.prop4_ + "\n" + "h.prop4 => " + h.prop4_ ); }); });
実行結果
f.toSource() => ({prop1_:4, prop2_:true, prop3_:[], prop4_:"blah"}) h.toSource() => ({prop3_:[]}) f.prop1 => 4 h.prop1 => 4 f.prop2 => true h.prop2 => true f.prop3 => [] h.prop3 => [] f.prop4 => blah h.prop4 => blah
循環参照について(イベントハンドラの設置)
$(function() { var menu = document.getElementById('my-menu'); $('#run').click(function() { attachEvt(menu); }); }); var attachEvt = function(element) { var mouseHandler = function() { /* whatever */ $('#debug').html('done.'); }; element.addEventListener('mouseover', mouseHandler, false); };
attachEvt
の中に mouseHandler
をネストさせている。
これは、ハンドラ(mouseHandler
) が呼び出し元の attachEvt
のスコープ内に閉じ込められることを意味する。
したがって、ハンドラは element
への参照を保持し続ける。
element
は menu
への参照を保持し、menu
は div#my-menu
への参照を保持する。
さらに div#my-menu
はハンドラへの参照を保持している。
これを循環参照(circular reference)という。
参考:Fabulous Adventures In Coding : What are closures?
element.addEventListener – MDC