今回は,スクリプトのパッケージ化について学んでいきます。JavaScriptコーディングの際に,案件ごとに同じような処理を幾度となく繰り返し作っていませんか? 同じ処理なのであれば,汎用的に作っておいて,それを繰り返し使えるようにすれば便利です。

 JavaScriptのコードは,書き方によって,その場限りの使い捨てにもなりますし,今後何度でも流用できる資産にもなりえます。できることなら,あなたがこれから記述するコードは,資産となるようにしたいものです。

jsファイルを処理ごとに分離する

 一つのページに様々なアクションを加える場合,一つのjsファイルにすべてを詰め込んでしまうと,コードが長くなりわかりにくくなります。また,保守性の観点からも,できれば,機能単位ごとにjsファイルを分離して,それぞれが独立して動作するように設計したいものです。

 しかし,ただ単にjsファイルを分離しただけでは問題が発生することがあります。ここではよくありがちなミスを見ていきましょう。

HTML


<script type="text/javascript" src="cs1.js"></script>
<script type="text/javascript" src="cs2.js"></script>

cs1.js


var msg = 'cs1';
var init = function() { alert(msg); };
var addListener = function(...) {...};
addListener(window, "load", init);

cs2.js


var msg = 'cs2';
var init = function() { alert(msg); };
var addListener = function(...) {...};
addListener(window, "load", init);

 このサンプルは,HTML側でcs1.jsとcs2.jsをロードしています。それぞれのjsファイルでは,ページがブラウザにロードされたらアラート・ウィンドウをポップアップさせる処理が定義されています。

 cs1.jsでは,変数msgに'cs1'という文字列をセットし,その内容をアラート・ウィンドウに表示させます。cs2.jsも同じ処理を行いますが,変数msgに'cs2'という文字列をセットしている点だけが異なります。

 このサンプルは,ページがブラウザにロードされたら,最初に'cs1'が掲載されたアラート・ウィンドウが表示され,その次に'cs2'が掲載されたアラート・ウィンドウが表示されることを期待したものです。

 cs1.jsとcs2.jsに定義されているaddListenerは,前回でご紹介したリスナセット用の関数オブジェクトです。詳細は,前回を参照してください。

 では,実際にこれをブラウザで実行させるとどうなるでしょうか。

 残念ながら,期待通りの結果にはなりません。2回アラート・ウィンドウが表示されるのですが,その掲載内容が,いずれも"cs2"になってしまいます。これは,cs1.jsとcs2.jsで使われているmsgという変数の名前が重複しているため,cs2.jsによってcs1.jsのmsgの値が上書きされてしまったのです。

 JavaScriptでは,jsファイルを分離したとしても,実際には,それらを一つに連結したものとして解釈されてしまいます。そのため,もし同じ変数名をそれぞれのjsファイルの中で使ってしまうと,あとからロードされたjsによって上書きされてしまいます。

 ただ単に,機能単位にjsファイルとして分離するだけでは問題が起こることが良くおわかりになったのではないでしょうか。これでは,ファイルをわざわざ分離した意味がありません。jsファイルを分離する意味は,それぞれが独立して機能するところにあります。

 これを解決するには,jsファイルの中身を,一つのオブジェクトでラッピングしてしまう方法が効果的です。

オブジェクトによるラッピング

 JavaScriptをコーディングする際,あまりオブジェクトという概念を意識してこなかったという方は多いのではないでしょうか。また,オブジェクトを難しくとらえている方も多いかもしれません。

 オブジェクトとは,ここでは,単なる箱と考えてください。すでに変数という概念はしっかりと理解されていると思いますが,オブジェクトも同じようなものです。しかし,オブジェクトは,名前を付けて,いくつでも,何でも詰め込めるという点が特徴です。文字列はもちろんのこと,関数も詰め込むことが可能です。

 まずは,オブジェクトの作り方を見ていきましょう。


/* オブジェクトを定義 */
var obj = new Object();
/* mynameという名前のプロパティを定義 */
obj.myname = '太郎';
/* saynameという名前のメソッドを定義 */
obj.sayname = function() { alert(obj.myname); };

 このサンプルでは,objというオブジェクトを新たに作り,その中にmynameという名前のプロパティと,saynameという名前のメソッドを定義したものです。

 オブジェクトのプロパティは,オブジェクト名にドットをつけ,プロパティやメソッドの名前を付け加えるだけで利用可能です。このサンプルでは,mynameプロパティを,saynameメソッド内のalert関数の引数として使っています。

 オブジェクトを定義する場合には,もう一つの書き方があります。


var obj = {
  myname: '太郎',
  sayname: function() { alert(obj.myname); }
};

 こちらはかなりすっきりとしたコードになりますね。どちらの書き方でも構いません。お好みに合わせて,ご自分のコーディングスタイルとしてください。本連載では,後者の記法を使います。

 では,オブジェクトに定義したメソッドの呼び出し方を見ていきましょう。


obj.sayname(); /* 太郎 と表示されたアラート・ウィンドウがポップアップ */

 このように,オブジェクト名にドットをつけ,メソッドの名前を付け加えるだけで,そのメソッドを実行することができます。メソッドを実行する場合は,メソッド名の最後に()を忘れないようにしてください。

 では,jsファイル内のスクリプトをオブジェクトでラッピングするとは,どういうことかを見ていきましょう。先ほどのサンプルをオブジェクトでラッピングすると,次のようになります。

cs1.js


var cs1 = {
  msg: 'cs1',
  init: function() { alert(cs1.msg); },
  addListener: function(...) {...}
};
cs1.addListener(window, "load", cs1.init);

cs2.js


var cs2 = {
  msg: 'cs2',
  init: function() { alert(cs2.msg); },
  addListener: function(...) {...}
};
cs2.addListener(window, "load", cs2.init);

 cs1.jsでは,cs1という名前のオブジェジェクトを使って,すべてをラッピングしました。同様に,cs2.jsではcs2という名前のオブジェクトを使っています。このように,jsファイルごとに一つのオブジェクトでくくってしまうことで,それぞれのjsファイルの処理が,お互いに影響を及ぼすことを回避しています。

 cs1.jsにあるmsgは,cs1.msgと明示的に指定しないと呼び出すことはできません。同様に,cs2.jsのmsgも,cs2.msgと明示的に指定しなければ呼び出されることはありません。こうすることで,それぞれのjsファイルの間で,変数名の重複を避けているのです。

 このサンプルを実際にブラウザで実行すると,期待通りの結果が得られます。

 このサンプルでは,オブジェクトの名前として,ファイルの名前と同じものを使いましたが,重複さえなければ,何でも構いません。