【JavaScript入門】クロージャって一体何?使い方まで徹底解説

こんにちは!Webコーダー・プログラマーの貝原(@touhicomu)です。

みなさん、クロージャって聞いたことありますか?
JavaScriptのコードを書く上で必要不可欠な知識です。

そんなの聞いたことない…
聞いたことあるけど、よくわからない…

今回は、そんな悩みを解決していきましょう。
JavaScriptのクロージャについて解説します。

この記事では下記の流れで、応用的かつ実践的な内容まで解説していきます。

【基礎】クロージャとは何か
【基礎】クロージャのメリットとは?
【基礎】クロージャの使い方
【発展】クロージャの活用例2つ
【発展】メモリリークには気をつけよう

クロージャは少し特殊なオブジェクトで、解説だけではイメージがしづらいかもしれません。
本記事では具体的なコード例をふんだんに使い、順を追ってしっかり解説します。

安心して、しっかり学習していってください!

クロージャの基本

クロージャとは関数とその関数が定義された状態をセットにした特殊なオブジェクトのことです。

ものすごくざっくり言うと、下記のように、変数の中に変数が入っている状態を指します。

これだけではちょっとわかりづらいですね。
クロージャと通常の関数のサンプルコードを比較しながら、違いを理解していきましょう。

クロージャとは?

通常、関数を書く時には下記のように記述しますね。

[通常の関数] [実行結果]

この関数では、timeDown()がなんど呼ばれても、タイマーの値が入った変数(time)は9のままです。

呼び出される都度、変数timeは10で初期化されたあとに、-1されるため、得られる結果は変わらず9のままになります。

では、クロージャを使った例を見てみましょう。

[クロージャ] [実行結果]

クロージャの例の場合、実行結果として9,8,7とtimer()が呼ばれるたびに結果が変わっているのがわかりますね。

これは、関数(createTimer())の中で定義された変数(time)と関数(timeDown())の結果が、セットで保存されているためです。

この、セットで保存されるという現象そのものクロージャです。

クロージャのメリットとは?

クロージャのメリットは、オブジェクトの変数やメソッドを他のプログラムから簡単に変更されないように制御できることです。

ソース量が多かったりチームで開発をする場合は、自分のプログラムで使用しているオブジェクトの変数の値などが他のプログラムで容易に書き換えできるようになっていると、予期せぬエラーやバグが発生する可能性があります

そのため、カプセル化と呼ばれる、オブジェクト内部で使用している変数やメソッドを容易に変更できない仕組みが存在しています。
クロージャはその仕組みの一役を担っているのです。

クロージャの使い方

では、クロージャはどのように記述するのかを確認していきましょう。

基本形は下記の形です。

varを使ってメソッドを変数として代入しているところがポイントです。

こうすることにより、クロージャとして、カプセル化して外部に公開しないメソッドにすることができます。

ちなみに、プロパティの場合は下記のように書くことでカプセル化することができます。

以上を踏まえて、サンプルコードを見ていきましょう。

上記の例では、各メソッドは全てクロージャになっているので、Helloオブジェクトの変数に自由にアクセスできます。

変数nameを決める関数は、getNameと、setNameの2つがあります。
このうち、getNameはカプセル化されていて外部からはアクセスできません

一方変数majorは、getMajorと、getMajor2つの関数のどちらも外部に公開されているので、外部からのアクセスが可能です

最後にsayメソッドが、メッセージをコンソールに出力します。

では、このコードを実際に動かしてみましょう。

実行結果

「ins.name = ‘Tim’」というコードは反映されていませんね。
これは、変数nameがカプセル化されていて、変更することができないからです。

しかし、setNameはカプセル化されていないので、変数nameの値を変えることができます。(実行結果の3行目の出力です)

ここまでで、クロージャがどのように動作するのかを確認できましたね。

次の項目では、クロージャのスコープについて解説していきます。

スコープってなんだ?という人は、まずは下記の記事をチェックしてみてください。
>>初心者必見!スコープ徹底解説

クロージャのスコープ

関数のスコープは、関数の定義された内側のスコープを関数のスコープとしますが、クロージャは少し違います。

クロージャでは、クロージャが定義された外側のスコープをクロージャのスコープとして使用することができるのです。

下記の例で、クロージャが定義された外側の変数にアクセスできることを確認してみましょう。

実行結果

outFuncに渡された”abc”がoutserFunc内で変数strに代入されていますね。
そして、クロージャclousureは、自身の外側にある変数strにアクセスし、出力を行なっています。

これはクロージャの特徴の1つで、普通の関数の場合は変数strにはアクセスできません。

即時関数のスコープ

即時関数とは、名前の通り即時に実行される関数のことです。
下記のように関数を書くと、即時関数としてすぐに実行されます。

詳しいことは下記の記事で解説していますので、併せて読んでみてください。

>>即時関数とは?どんな時に使うのかまで解説

では、この即時関数でのクロージャのスコープについて解説していきましょう。

クロージャを即時関数内のスコープで定義すると、クロージャは即時関数内のスコープの変数にアクセスできます

しかし、即時関数外のスコープからは、即時関数内のスコープの変数にはアクセスできません

例を見てみましょう。

実行結果

クロージャは即時関数内の変数にアクセスできていますね。(実行結果1行目)

一方、即時関数外のスコープから即時関数内のスコープの変数にアクセスしようとするとエラーが出てしまうのです。(実行結果2行目)

クロージャを使う際には、どこのスコープが使えるのかを注意するようにしましょう。

クロージャの実践

では、クロージャを使った実用的な例を見ていきましょう。
もちろん、クロージャの活躍箇所はここで紹介した例だけではありません。

例を参考に、ぜひ色々な組み合わせで試してみてください。

onloadと組み合わせてみよう

データが読み込まれたタイミングで実行されるonloadイベントにクロージャを設定する方法は色々あります。
そのうちの一つとして、関数の戻り値にクロージャを渡すという方法があります。

クロージャ内では、渡す側の関数の変数にアクセスできるからです。

以下、例を見ながら確認していきましょう。

実行結果

上の例では、onloadにmakeOnLoadCallback関数を定義しています。

makeOnLoadCallback関数の戻り値はクロージャになっていますので、onloadが実行される際は実質上クロージャがコールバックされます。

さらに、makeOnLoadCallbackの引数nameに値(myOnLoad)を渡していて、クロージャはそれを参照してコンソールへ出力するという仕組みになっています。

onclickと組み合わせてみよう

クリックした時に発火するonclickイベントにクロージャを設定する例として、まず、HTMLの方を見てみましょう。

以下のHTMLのdivタグをクリックの対象とします。

次に、前項と同様に関数の戻り値としてクロージャを渡していきます

実行結果(divを3回クリックした場合)

こうすることで、HTMLで設定したdivタグのエリアをクリックする度に、コンソールに引数msgの値とともにメッセージを表示することができるのです。

メモリリークするクロージャの書き方

プログラムコードを組んでいると、メモリの解放し忘れであるメモリリーク(メモリ漏れ)が起きるコードを書いてしまうことがあります。

メモリがリークすると、リークすればリークした分だけサーバーやパソコンが搭載しているメモリの使用量がどんどん大きくなってしまいます

そして最後にはメモリの空き容量がなくなってサーバーやパソコンがダウンして止まってしまうのです

クロージャにより外側のスコープの変数をうっかり保持したままでいると、クロージャが存在している間中ずっとその変数がメモリ内に残り続けてしましまいます

これがメモリリークです。

特に、スコープの関係上、どの変数をうっかり保持したままなのかわかりづらい場合もありますのでやっかいです。

以下の例では、bigDataという大きなメモリ容量を占めるデータを、オブジェクトmakeRefHavingClosure経由でクロージャがずっと保持し続けています

実行結果

上の例では、bigDataを途中でdeleteしてメモリ上から削除していると見せかけて、クロージャがまだ参照しているため、メモリからは削除されていません

結果、エラーが出てしまっていますね。

この現象に陥らないよう、クロージャを使う際には十分注意しましょう

まとめ

いかがでしたか?

今回の記事のポイントは

クロージャスコープの関係上、関数より利便性が高い場合があり、便利な関数です。

・クロージャは、オブジェクトのカプセル化に非常に役立ちます。

・クロージャをイベントハンドラに登録し、コールバックさせることもできます

・クロージャは扱い上、メモリがリークしてしまう危険性があります。

という点です。

クロージャは、JavaScriptのコードを組む上で必須の知識です。
ポイントを抑えて、しっかりと覚えましょう。

うっかり忘れてしまったら、またこの記事を読み返してください!

LINEで送る
Pocket

最短でエンジニアを目指すなら侍エンジニア塾

cta_under_bnr

侍エンジニア塾は業界で初めてマンツーマンレッスンを始めたプログラミングスクールです。これまでの指導実績は16,000名を超え、未経験から数多くのエンジニアを輩出しています。

あなたの目的に合わせてカリキュラムを作成し、現役エンジニア講師が専属であなたの学習をサポートするため効率よく学習を進めることができますよ。

無理な勧誘などは一切ありません。まずは無料体験レッスンを受講ください。

無料体験レッスンの詳細はこちら

書いた人

貝原 輝昌

貝原 輝昌

こんにちは!貝原(@touhicomu)と申します。
現在は、Web業界のフリーランスとして、主にPHP/WordPress/BuddyPress/VPSサーバー構築などの業務を受注しています。
現住所は、日本の西海岸、長崎県は波佐見町です。田舎ライフです。^^
地元の観光団体「笑楽井石」にボランティアでほたる撮影会やそば塾などのスタッフとして参加させて頂いています。
以下の活動も行っています。
 ・笑楽井石のブログ
 ・エクセル関数を日本語化するソフト
 ・エクセルVBAを日本語で記述するソフト

おすすめコンテンツ

あなたにぴったりなプログラミング学習プランを無料で診断!

プログラミング学習の効率を劇的に上げる学習メソッドを解説