【JavaScript】varとletのスコープについて調べてみた

Javascriptでの変数の定義方法にはvar、let、constの3つがあります。この中でもvarとletの使い方で、ちょっと前に実装で先輩から「これはバグになる可能性がある」と注意を受けたので、備忘録として書き残そうと思います。

2つの違い

varとletの違いは2つあります。「使用できる範囲(スコープ)」です。今回は「分岐(if)」「ループ(for)」「例外処理(try~catch)」「関数(function)」の4つについて、内部で変数を定義した場合にどのような動きになるかを見てみましょう。

分岐(中で定義)

if文内で変数の定義し、if文の中と外で参照してみます。

var

if文の中にvarで変数定義すると、外でも使えます。

if (true) {
    var test = "moji";
    console.log("test1 = " + test);
}
console.log("test2 = " + test);

// --> 
// test1 = moji
// test2 = moji

let

if文の中にletで変数定義すると、外では使えずエラーになります。

if (true) {
    let test = "moji";
    console.log("test1 = " + test);
}
console.log("test2 = " + test);

// --> 
// test1 = moji
// Uncaught ReferenceError: test is not defined

分岐(後に定義)

if文の後で変数の定義し、if文の中と外で参照してみます。

var

if文の中にvarで変数定義すると、外でも使えます。

if (test) {
    test = 'moji';
    console.log("test = " + test);
} else {
    console.log("test = " + test);
}

var test;

// --> test = undifined.

let

if文の中にletで変数定義すると、外では使えずエラーになります。

if (test) {
    test = 'moji';
    console.log("test = " + test);
} else {
    console.log("test = " + test);
}

let test;

// --> Uncaught ReferenceError: Cannot access 'test' before initialization

ループ(中で定義)

for文の中でも変数を定義をしてみます。中と外でどのように参照できるでしょうか。

var

for文の中にvarで変数定義すると、外でも使えます。

for (let n = 0; n < 2; n++) {
    var test = "moji";
    console.log("test1 = " + test);
}

console.log("test2 = " + test);

// -->
// test1 = moji
// test1 = moji
// test2 = moji

let

for文の中にletで変数定義すると、外では使えずエラーになります。

for (let n = 0; n < 2; n++) {
    let test = "moji";
    console.log("test1 = " + test);
}

console.log("test2 = " + test);

// -->
// test1 = moji
// test1 = moji
// Uncaught ReferenceError: test is not defined

ループ(条件内で定義)

ループでの条件で使用する変数についても試してみます。

var

for文の条件でvarの変数定義すると、外でも使えます。

for (let n = 0; n < 2; n++) {
    var test = "moji";
    console.log("test1 = " + test);
}

console.log("test2 = " + test);

// -->
// test1 = moji
// test1 = moji
// test2 = moji

let

for文の条件でletの変数定義をすると、外では使えずエラーになります。

for (let n = 0; n < 2; n++) {
    let test = "moji";
    console.log("test1 = " + test);
}

console.log("test2 = " + test);

// -->
// test1 = moji
// test1 = moji
// Uncaught ReferenceError: test is not defined

例外処理(中で定義)

try~catch文でも同様に内部で定義してみて、その外で参照してみます。

var

tryの中にvarで変数定義すると、外でも使えます。しかし、上記では入っていませんが、testの定義前で例外が発生すると、変数が定義されていないためtest2の出力でエラーになります。

try {
    var test = "moji";
    console.log("test1 = " + test);
} catch (err) {
    console.log("error: " + err.message);
}

console.log("test2 = " + test);

// -->
// test1 = moji
// test2 = moji

let

tryの中にletで変数定義すると、外では使えずエラーになります。

try {
    let test = "moji";
    console.log("test1 = " + test);
} catch (err) {
    console.log("error: " + err.message);
}

console.log("test2 = " + test);

// -->
// test1 = moji
// Uncaught ReferenceError: test is not defined

関数(中で定義)

function関数の内部でも変数の定義を行い、外部で参照してみます。

var

functionの中にvarで変数定義すると、外では使えずエラーになります。

function testFunc() {
    var test = "moji";
    console.log("test1 = " + test);
}

testFunc();
console.log("test2 = " + test);

// --> 
// test1 = moji
// Uncaught ReferenceError: test is not defined

let

functionの中にletで変数定義すると、外では使えずエラーになります。

function testFunc() {
    let test = "moji";
    console.log("test1 = " + test);
}

testFunc();
console.log("test2 = " + test);

// --> 
// test1 = moji
// Uncaught ReferenceError: test is not defined

まとめ

上記の内容をまとめると以下のようになります。内部で定義した場合、外でも使用できるときは「〇」、外では使えずエラーが発生する場合は「✕」で示します。

文法定義位置var let
分岐(if)内部
分岐(if) 外部〇(ただしundifined)
ループ(for)内部
ループ(for)条件文
例外処理(try~catch)内部
関数(function)内部

この結果から、関数以外の内部でvarを使用した変数定義を行わない方がよいことがわかります。varを使った場合、外部で上書きができてしまうため、注意です。条件文やループ文内で変数定義する場合はletを使うことをお勧めします。

個人的にはちゃんと違いも分からず使用していた節がありました。基本的はletを使用したいと思います。