特集PC技術

コアJavaScript(ECMAScript)の図書室

 

31冊目 【JavaScript】[ECMAScript] クロージャ

クロージャとは関数が定義された環境への参照を持った関数のことです。
最初にこのような説明を見たときは正直言って意味不明でした。
ある程度調べたり、実際に組んでいてようやく理解ができたくらいです。
クロージャを理解するためには、以下のことを先に理解している必要があります。
関数はデータ型の1つであること、関数の中に関数が定義できること、スコープチェーンの存在
これらのことがわかっている前提で説明をしていきます。

目次

[1] クロージャってこんなもの

クロージャに関していえば、説明をするよりも前にプログラムを見たほうが早いと思うため
解説をする前にまずは以下のサンプルを確認してください。

function closure_sample(){
	var x = 0;

	// 戻り値用の関数
	function data_get(){
		return ++x;
	}

	return data_get;
}

プログラム自体は長くないのだが、この実行状態を理解しなくてはなりません。
上記プログラムで注目してもらいたいのは、戻り値を関数(data_get)としていることです。
この関数(data_get)は外側の関数(closure_sample)内にある関数なので、関数内関数です。
関数内関数(data_get)は外側の関数(closure_sample)が持つ変数(x)を参照できます。
この変数(x)はローカル変数でもグローバル変数でもないため、レキシカル変数といわれています。
このレキシカル変数の動作に次は注目してください。

[2] クロージャを使うと何ができるのか

クロージャを作成しましたのでクロージャというものがどのような動作をするのか見てください。
クロージャの動作を比較するため、いくつかのパターンと比較して説明をします。

// レキシカル変数を持つ関数
function closure_sample(){
	// レキシカル変数(data_get関数から見て)
	// ここの変数は実行後の環境を戻り値として返しているので
	// 処理が終わったとしても値を保持できる
	var x = 0;

	// 戻り値用の関数
	function data_get(){
		return ++x;
	}

	return data_get;

}

// レキシカル変数を持たない関数
function closure_sample2(){

	// 戻り値用の関数
	function data_get(){
		// ローカル変数
		var x = 0;
		// return文(++x)が実行されるとローカル変数(x)は解放されてしまう
		return ++x;
	}

	return data_get;

}

// ローカル変数とレキシカル変数の動作の違いを確認してみよう
var data = closure_sample();
var data2 = closure_sample2();

// レキシカル変数を持つ関数を実行してみる
// 実行するたびに表示される値が変わるのがわかる
// 変数dataが実行後の環境を持っているということである
document.writeln(data());	// 1
document.writeln(data());	// 2

// ローカル変数を持つ関数を実行してみると値が変わらないのがわかる
// data_get()関数が実行された後変数(x)が解放(初期化される)されるため
// レキシカル変数とローカル変数の違いがわかるはず
document.writeln(data2());	// 1
document.writeln(data2());	// 1

// おまけ
// 変数に代入せずにそのまま実行(書き方はこんなになるけど)
// 何度実行しても表示される値が変わらない
// 実行後の環境が保持されていないためである
document.writeln(closure_sample()());	// 1
document.writeln(closure_sample()());	// 1

関数はデータ型の1つなので変数に代入することができます。
このときの変数(data)がとても大切です。
それはこの変数(data)が関数が定義されている環境を持つことです。
通常の関数だった場合、関数内に定義されている変数(x)は関数が終了した後に開放されます。
しかし、変数(data)は関数の定義情報を持ったまま(関数が戻り値として戻ってくるので)なので
変数(x)の値が変更されたまま持つことができるわけです。
変数に代入せずに実行した場合は、実行された後の環境を持っていないのでレキシカル変数であっても解放されます。

[3] クロージャは別々の値を持つことができる

クロージャは別々の値を管理することができます

// レキシカル変数を持つ関数
function closure_sample(){
	// レキシカル変数(data_get関数から見て)
	// ここの変数は実行後の環境を戻り値として返しているので
	// 処理が終わったとしても値を保持できる
	var x = 0;

	// 戻り値用の関数
	function data_get(){
		return ++x;
	}

	return data_get;

}

// 同じ環境のクロージャを持つ変数を2つ作成してみた
var data = closure_sample();
var data2 = closure_sample();

// 別々に値を所持していることがわかる
document.writeln(data());	// 1
document.writeln(data2());	// 1
document.writeln(data());	// 2
document.writeln(data2());	// 2

このプログラムで2つの変数(dataとdata2)は同じ値を持っているのでかぶってしまうと思われがちです
しかし、JavaScriptの関数は関数が実行されるごとに別々の環境を持つためこのような動作になります。
このような動作をするためオブジェクトのような働きをします。

[4] 更新履歴

日付 詳細
2011/08/26 コンテンツ公開

コメントの投稿


画像の中に見える文字を入力してください。

トラックバックURL

http://www.isl.ne.jp/cgi-bin/mt/mt-tb.cgi/1701

サイト内検索

コアJavaScript(ECMAScript)の図書室

twitterTwitter

最新ブログ記事

2015年06月13日

JavaScriptの情報室

4冊目 ハイブリッドアプリが作れる。tabris.js

2014年10月16日

C++言語学習録

第3回 ファイルを分割してみよう

2014年10月09日

C++言語学習録

第2回 オブジェクト指向の復習

2014年10月02日

C++言語学習録

第1回 C++の開発環境を整える

2014年09月25日

Swiftを試してみようなう。

第4回 Swiftでクラスを使うなう。

ISL 情報戦略研究所

pagetopこのページの先頭へ戻る