###背景###
皆さんはjavascriptのletとvarのスコープ(変数の有効範囲)の違いがご存知だと思います。その違いを表せる一つの例を紹介します。この例はよく面接にも出ます。
####varを使う場合1####
####コード####
const main = () => {
const funcs = []
for (var i=0;i<5;i++) { <--ここのiをvarで定義しています
funcs.push(()=>{
console.log(i)
})
}
for (let func of funcs){
func()
}
}
main()
####結果####
5
5
5
5
5
####letを使う場合####
####コード####
const main = () => {
const funcs = []
for (let i=0;i<5;i++) { <--ここのiをletで定義しています
funcs.push(()=>{
console.log(i)
})
}
for (let func of funcs){
func()
}
}
main()
0
1
2
3
4
###解説###
case1とcase2の違いは一箇所だけです。それはforの中,iを宣伝する方法は違います。
case1はvarを使っています。
case2はletを使っています。
そして出力はcase2、for分にletを使っているコードは予想通りに動いていました。
ここで三つの知識点を説明いたします。
1. varを使うと、スコープはfor文ではなく、for分の上のmain関数になります
2. letを使うと、スコープはfor分になり、毎回ループするときに、新しいスコープを作っています
3. クロージャーは直上のスコープにアクセスしています
varを使うと、for分ごとにクロージャーを作っていますが、クロージャーからアクセスできる直上のスコープはmain関数のスコープです。main関数スコープのiはfor分のループと共に変化され、最終形態はi=5になります。closures配列にある五つのクロージャーから出力しようとしたiもmain関数スコープにあるiになり、すべてのクロージャーは同じiをアクセスし、すべてのクロージャーは同じ値を出力しています。
letを使うと、新しいスコープもループと共に作成されています。毎回のループにあるiは新しく作成されたスコープにあります。それぞれのクロージャーはアクセスできるスコープは直上のスコープはmain関数のスコープではなく、新しい作成されたfor分のスコープです。closures配列にある五つのクロージャーからアクセスできるスコープも五つにあり、それぞれ独立されています。それぞれ独立されてあるスコープにあるiはそれぞれ異なるため、当然クロージャーからの出力はそれぞれ異なります。
###感想###
どんな言語も深く考えれば、楽しみを味わえます。