かつのりの日記2

わんくまでは珍しいJavaを中心とした日記です

目次

Blog 利用状況

書庫

日記カテゴリ

いろいろリンク

JavaScriptでのクロージャの使い方

次はJavaScriptでのクロージャについてです。よく間違えやすいケースについて書いてみます。

まずは、シナリオを考えます。

  • ボタンが3つある
  • 1番目のボタンをクリックしたら1をアラートで表示
  • 2番目のボタンをクリックしたら2をアラートで表示
  • 3番目のボタンをクリックしたら3をアラートで表示

これをコードにしてみましょう。まずはHTMLだけ。


<html>
<head>
<script type="text/javascript">
</script>
</head>
<body>
<form name="foo">
<input type="button" id="button1" value="1">
<input type="button" id="button2" value="2">
<input type="button" id="button3" value="3">
</form>
</body>

次にScriptタグの中身を書いて見ましょう。


function button1Click(){
	alert(1);
}

function button2Click(){
	alert(2);
}

function button3Click(){
	alert(3);
}
//前提として、bodyタグにonload="windowOnload()"が記述済みとする
function windowOnload(){
	var button1 = document.getElementById("button1");
	button1.onclick = button1Click;
	var button2 = document.getElementById("button2");
	button2.onclick = button2Click;
	var button3 = document.getElementById("button3");
	button3.onclick = button3Click;
}

これは取り合えず非常に冗長ですが、オーソドックスな書き方です。ではこれをクロージャで書いてみましょう。


window.onload = function(){
	document.getElementById("button1").onclick = function(){
		alert(1);
	};
	document.getElementById("button2").onclick = function(){
		alert(2);
	};
	document.getElementById("button3").onclick = function(){
		alert(3);
	};
};


少しシンプルになりました。色々なライブラリとのコンフリクションがあったりするので、最近のJavaScriptの主流の書き方では、極力グローバルの変数や関数を使いません。

ただこれでもまだ冗長なので、普通に以下のようなコードを書かれると思います。


window.onload = function(){
	for(var i = 1; i <= 3; i++){
		var button = document.getElementById("button" + i);
		button.onclick = function(){
			alert(i);
		};
	}
};

これを実行すると分かると思いますが、どのボタンをクリックしても4が表示されます。ループの中のクロージャはループカウンタの変数iに対する参照を持ちますが、最後に評価された結果の4が全てのクロージャから参照されてしまうのです。

では、上記コードに手を入れて正しく動くようにしてみます。


window.onload = function(){
	for(var i = 1; i <= 3; i++){
		var button = document.getElementById("button" + i);
		//もう一つ関数で囲む
		(function(n){
			button.onclick = function(){
				alert(n);
			};
		//iを渡す
		})(i);
	}
};


匿名関数にiを都度渡していますが、これによってiを参照しなくなります。関数に渡された仮引数を都度参照するので、この場合正しく動きます。

では最後に効率化を行いましょう。関数リテラルはキャッシュされず、インタプリタによってそのステートメントが評価されるたびに新しい関数オブジェクトが作られます。すなわち"(function(n){"で始まる関数のオブジェクトは都度つくられるのです。


window.onload = function(){
	var click = function(n, button){
		button.onclick = function(){
			alert(n);
		};
	};
	for(var i = 1; i <= 3; i++){
		var button = document.getElementById("button" + i);
		click(i, button);
	}
};


関数は都度作られるというのは、以下のコードで確認できます。


var list = [];

for(var i = 0; i < 3; i++){
    list.push(function(){});
}

alert(list[0] == list[1]);
alert(list[1] == list[2]);
alert(list[0] == list[2]);


上記は全てfalseになります。

投稿日時 : 2008年1月28日 1:15

Feedback

# re: JavaScriptでのクロージャの使い方 2008/01/28 15:15 かずくん

Behavior.js使って、HTMLからjavascriptコードを消し去るときに、closureを使いました。

> 前提として、bodyタグにonload="windowOnload()"が記述済みとする
の部分すら、HTMLから除去して、jsファイルで定義できる優れもの。

# re: JavaScriptでのクロージャの使い方 2008/01/29 21:06 まじかんと

> 関数は都度作られる

同じ関数リテラルを複数回評価した場合、特定の条件を満たせば同じ関数オブジェクトを再利用することが仕様上認められていたと思います。つまり、処理系によっては「上記は全てfalseにな」らない可能性があると思います。

# re: JavaScriptでのクロージャの使い方 2008/01/29 23:50 かつのり

>まじかんとさん
そういう処理系もあるんですね。参考になります。
必ずfalseになるという記述はまずいですね。

# XxdHeVcqnqe 2011/09/29 10:49 http://oemfinder.com

xxj6DR I am getting married on the 15th of November. Congratulate me! Then will be here rarely!...

# ViQFKMZREqfkxQJxnOW 2011/11/29 20:36 http://castellohair.com/

I am getting married on the 15th of November. Congratulate me! Then will be here rarely!...

# xLVOEeEkXxfgInczlkA 2011/12/22 20:01 http://www.discreetpharmacist.com/

lzfAMF Author, keep doing in the same way..!

# re: JavaScriptでのクロージャの使い方 2012/08/03 12:57 masa

クロージャと無名関数を勘違いしてませんか?

(function(n){
を含むコードでbuttonが関数外部で宣言されているため偶然にもクロージャ(に近いもの?)ができていますが、本記事で紹介されている「クロージャ」なるものはただの関数内で宣言された無名関数です。

「javascript クロージャ 使い方」でggったらこのページが一番上に表示されてしまって正直これはまずいと思ったのでコメントさせていただきます。

http://dqn.sakusakutto.jp/2009/01/javascript_5.html
こちらの記事がわかりやすいと思いますので参照してみてください。

# JavaScript | SanRin??? 2012/08/14 23:01 Pingback/TrackBack

JavaScript | SanRin???

# re: JavaScriptでのクロージャの使い方 2012/08/28 23:33 通りがかり

masa 様の言うとおり、このページが上に表示されているにもかかわらず、内容は首をひねるものでした。

クロージャを理解しようとしている人が見て勘違いされては困ると思い、私もコメントさせていただきます。

# re: JavaScriptでのクロージャの使い方 2013/02/13 18:51 trshugu

これはクロージャでは・・・ない!!!

タイトル  
名前  
Url
コメント