前提知識
- ES2015より前(var宣言のみの時代)において、JavaScriptのブロック・レベルのスコープは存在しない
- ES2015以降のlet,constはブロックスコープを定義する
letやconstが使えるようになってもクロージャは現役。 プライベート変数としての役割が大きい。 クラスでも同様のことが可能だが、シンプルな関数の場合はクロージャで実装したい。
グローバル変数を使わずに関数に「状態」を持たせる(疑似プライベート変数)
第3回 変数の宣言とスコープ (4/4):連載:Ajax時代のJavaScriptプログラミング再入門 - @IT
クロージャとは、ひと言でいうならば、「ローカル変数を参照している関数内関数」のこと。
基本的なクロージャ
function counter(num) {
let cnt = num;
return function() {
return cnt += 1;
}
}
let myCounter = counter(10);
myCounter() >> 11
myCounter() >> 12
複数のクロージャ
function counter(num) {
let cnt = num;
return {
increment: function() {
return cnt += 1;
},
decrement: function() {
return cnt -= 1;
}
}
}
let myCounter = counter(10);
myCounter.increment() >> 11
myCounter.decrement() >> 12
varを使った問題のコード
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
var i = 0
for (; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
letを使ったコード
letはブロックスコープを定義します。
for (let i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
console.log(i) >> i is not defined
ブロックスコープが実装されていますね。
forの外でiを参照したい(一致した配列のインデックスを知りたい等の)場合は、forの外側でiを定義します。
let i = 0;
for (; i < 3; i++) {
if(i===2) break;
setTimeout(function() {
console.log(i);
}, 1000);
}
console.log(i) >> 2
まとめ
クロージャを使うことでプライベート変数を簡潔に記述できます。 また、今まではまでクロージャを使わないといけなかった箇所をletを使えば解決する例もあります。