初心者向けのjQuery入門講座|デザイナー向けのJavaScriptライブラリ

このエントリーをはてなブックマークに追加
索引
1章:jQuery入門
2章:jQuery基礎
3章:jQuery発展
番外編:研究

クロージャ(1)

概要

Flashのムービークリップを目指して

FlashのActionScriptがデザイナーにも扱いやすい理由の1つに、ムービークリップ毎にスクリプトを分けて書けるというのがあります。その反面javaScript( jQueryを含む)は、1カ所に全てのスクリプトを書かなければならず、大規模なコンテンツではFlash以上に管理が大変になります。
※外部jsファイルは単純にファイルを分けているだけです。

しかしjavaScriptでもムービークリップっぽい仕組みができそうな気がしたので色々調べました。するとクロージャという仕組みが使えそうな気がするのです。まだ、どの様にこれを活用すべきか分からないのですが、ここにまとめておこうと思います。
※クロージャはjQueryの仕組みではなく、javaScriptが元々持っている仕組みのためjQueryでも活用できます。

解説

なるべくローカル変数を利用する

Flashではムービークリップを分けてしまえば、同じ名前でも別の変数として扱われます。しかしムービークリップがないjavaScriptでは、その方法は利用できませんからローカル変数を上手く利用しなければなりません。

実装の隠蔽 (カプセル化)
複雑で量の多いプログラムは、処理が他に影響して思わぬバグを招く危険性があります。なので、各処理は簡単にアクセスできないように隠蔽します(処理が隔離されるのでカプセル化とも呼ばれます)。利用範囲の狭いローカル変数を活用するのもこのためです。

サンプル(closure1/01.html)を開いて下さい。「count up」ボタンをクリックすると緑色のdiv内に表示した数字が増えていきます。「watch」ボタンをクリックするとアラートで現在の数字を表示します。スクリプトは以下の様にシンプルになっています。

readyイベントで設定してあるfunction内でvarを利用して変数cntが作成されているので、変数varは、このfunction内のローカル変数です。

$(function () {
	var cnt = 0;			
	$(".up").click(function () {
		cnt++;
		$(".cnt").text(cnt);
	})			
	$(".disp").click(function () {
		alert(cnt);
	})
})

しかしjQueryでは多くのコードをこのreadyイベントのfunction内に書くので、このレベルでは不十分です。さらに変数cntをローカルにすることは可能でしょうか?まずは安直な方法で作成したサンプル(closure1/02.html)を開いてjQueryを確認してください。

ポイントは新規にfunction「countMaster」を作成し、その中にローカル変数を作成した点です。そしてupボタンをクリックしたら、このcountMasterを実行してカウントアップするようにしています。

$(function () {
	function countMaster() {
		var cnt = 0;
		cnt++;
		$(".cnt").text(cnt);
	}
	$(".up").click(function () {
		countMaster();
	})
	$(".disp").click(function () {
		alert(cnt);
	})
});

このサンプルでは変数cntはさらに深いfunctionで作成され、よりローカル度が高くなりました。しかしupボタンをクリックしても1以上に増えませんし、watchボタンをクリックしても数字が表示されません。

それは当たり前で、変数cntをcountMaster内のローカル変数にするためにはcountMaster内で変数を作成しなければなりません(上記ソース3行目)。そのためcountMasterが実行される(upボタンがクリックされる)度に変数cntが作成(初期値は0)されるため値が1から増えないのです。

そして変数cntはcountMasterのローカル変数のため、countMasterの外からは値を取得できません。そのためwatchボタンをクリックしても値が表示されないのです。

そこでクロージャの登場

しかしクロージャを利用すれば、この問題が解決できます。サンプル(closure1/03.html)を開いて、ソースの以下の部分を確認して下さい。ここではカウントアップさせる機能のオリジナルを作成しています。Flashでいうと、これがムービークリップのシンボルと考えて下さい。

//---クロージャ(オリジナルの作成)
function countMaster() {
	var cnt = 0;
	function countUpFunc() {
		cnt++;
		$(".cnt").text(cnt);
	}
	return countUpFunc;
}

この部分は以下の様な構造になっています。サンプルでは1回実行すればよいことは変数cntの作成です(上記ソース3行目:初期値は0)。そして利用したい機能はcountUpFuncとしてfunctionを作成しました。最後にreturnで作成したfunctionを返します(returnはfunctionも返すことができるのです)。

クロージャ(オリジナル)の構文
function 任意の名前() {

 最初に1回実行すれば良いことを書く(初期化)

 function 任意の名前() {
  利用したい機能はfunctionを作成してその中に書く
  サンプルではカウントアップして表示する機能
 }

 return 作成した上記function名
}

Flashではシンボルをもとに作成されたインスタンスを利用してコンテンツを作成します。クロージャも同じでインスタンスを作成しなければなりません

このサンプルでは以下の部分でインスタンスを作成しています。オリジナルとして作成したcountMasterを実行して得たものを変数cntFuncに代入します。このcntFuncがcountMasterのインスタンス(コピー)です。この処理によってオリジナルが実行されます。つまり変数cntが作成され、カウントアップさせる機能(countUpFunc)が返値としてcntFuncに代入されます。

//---クロージャ(インスタンスの作成)
cntFunc = countMaster();

そしてサンプルでは、作成したインスタンスは以下の部分で利用しています。作成されたインスタンスcntFuncにはcountUpFuncの機能が代入されていますから、このように実行するだけでカウントアップされます。

$(".up").click(function () {
	cntFunc();
})

実際にサンプルで「count up」ボタンをクリックすると数字が増えていきます。これでサンプル05/03_02.htmlで実現できなかった、より深いfunctionで作成されたローカル変数を利用できるようになりました。

変数cntがcountMasterのローカル変数である確認として、watchボタンをクリックしても値が表示されません。これは正しい仕様なのですが「変数cntをcountMaster外から取得したい」場合は、どうすればよいのでしょうか?次回説明したいと思います。