2019-05-13
クロージャーとは?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は外部からはアクセスできないため影響は受けません。
このように変数を隠蔽することができるので、意図せず書き換えられてしまう恐れがなくなり、他への影響を気にする必要もありません。
まとめ
生成するインスタンスが少なければ問題ないとは思いますが、プロジェクト内で多くのインスタンスを生成する場合などでは、コードの可読性や保守性に差がでてくると考えます。そのようなケースではクロージャーを活用しましょう。