2019-05-13

クロージャーとは?JavaScriptのスコープを理解し変数をに扱おう

javascriptに関する記事

クロージャーは、JavaScriptにおいて非常に強力な概念であり、関数型プログラミングやモジュール化など、さまざまなシナリオで活用されます。

今回はサンプルコードを通してクロージャーの解説を行います。クロージャーの基本を理解していきましょう。

変数のスコープ(範囲)の基本的な性質

javascriptで変数を扱う場合、「スコープ」を理解する必要があります。スコープとは範囲のことで、プログラム全体からアクセスできる「グローバルスコープ」と、関数内などの特定の範囲のみアクセスが有効となる「ローカルスコープ」の2種類があります。早速サンプルをもとにそれぞれのスコープの動作をみてみましょう。

var a = 100;//グローバル

function add() {
  var b = 5;//ローカル
  console.log(a + b);
}
add(); // 105
console.log(a + b); // Uncaught ReferenceError: b is not defined

変数aはグローバルスコープなので関数add内でもアクセス可能です。しかし、変数bはローカルスコープ(関数add内に定義)なので、関数外からのアクセスはできずエラーが発生してしまいます。

varは変数を定義するという宣言なので、あるローカルスコープ内のみで変数を上書きするということもできます。またvarが付いてなければ、スコープに関係なく同じ名前の変数に上書きがされます。次のサンプルコードではローカルスコープ内で、グローバルスコープで定義した変数の値を上書きしています。

思わぬ動作を引き起こしてしまうので、変数定義の際は理由がない限りvarを付けましょう。

スポンサーリンク

クロージャーとは

スコープの基本を理解したところで、クロージャーについてみてゆきましょう。javascriptにおけるクロージャーは「関数の中で関数を定義し、その関数の中で変数を定義する」ことで実現できます。下記のサンプルは、クロージャーを使わないカウンターです。

var count = 0;// グローバル
function counter() {
  count++;
  console.log(count);
}

counter(); // 1
counter(); // 2

同じく、クロージャーを用いずにカウンターを2つに増やしてみましょう。

var count1 = 0;
function counter1() {
  count1++;
  console.log(count1);
}

var count2 = 0;
function counter2() {
  count2++;
  console.log(count2);
}

counter1(); // 1
counter1(); // 2
counter2(); // 1
counter2(); // 2
count1 = 100;// ①
counter1(); // 101
counter1(); // 102

このように複数の関数を用意することで、カウンターを追加することができました。しかし、さらに3つ、4つと増やして行く場合、関数もcounter3、counter4と追加する必要があり、ソースコードの保守性を損なってしまいます。また、変数count1、count2はグローバル変数なので、どこからでも上書き可能で、意図せず書き換えられてしまう可能性があります(①)。

それでは上記のカウンターを、クロージャーを用いて書き直してみましょう。

function createCounter() {
  var count = 0;
  return function() {
    count++;
    console.log(count);
  };
}

var counter1 = createCounter();
counter1(); // 1
counter1(); // 2
counter1(); // 3

var counter2 = createCounter();
counter2(); // 1
counter2(); // 2

count = 100;

counter1(); // 4

クロージャーを使わない例ではcountがグローバル変数でしたが、クロージャーを用いた上記の例では関数createCounter内で定義され、外部からアクセスできないよう隠蔽されています。

これにより、変数counter1とcounter2を定義する際に、異なるローカル環境が作成され、それぞれの変数countは別々に参照されます。また、先ほど上書きされたように、count = 100としても、関数createCounterの中にある変数countは外部からはアクセスできないため影響は受けません。

このように変数を隠蔽することができるので、意図せず書き換えられてしまう恐れがなくなり、他への影響を気にする必要もありません。

まとめ

生成するインスタンスが少なければ問題ないとは思いますが、プロジェクト内で多くのインスタンスを生成する場合などでは、コードの可読性や保守性に差がでてくると考えます。そのようなケースではクロージャーを活用しましょう。