(初心者向け) JavaScript のブロック: let と var


概要

ECMAScript6 の仕様が盛り込まれた JavaScript (Node.js 8.9.0LTS など) では「ブロック」というのが導入され、変数の宣言に var の他 let での宣言が使えるようになっています。また、ブロックとは直接関係ありませんが、定数宣言用の const も同様に導入されています。

const, let, var の例

const PI = 3.141593;
let i = 0;
var s = "?";

ブロックとは

ブロックとは { } で囲まれたコードです。for 文などを書くとき繰り返し部分を { } で囲みますが、それがブロックです。ブロックは単独でも作れます。つまり、for や while などは必須ではありません。

var と let の違い

ブロック外の var で宣言した変数はブロック内でも有効です。スクリプトの一番外側で var で宣言した変数はグローバルになります。

let で宣言した変数はブロック内でのみ有効です。
ブロック外で var で宣言した変数と同じ名前の変数を let を使ってブロック内で宣言できます。
このような変数は、ブロック外で宣言した変数と同じ名前でも、別の(スタック上の)メモリに割り当てられるので、ブロック内でローカルに使用できます。
つまり、ブロックから出れば、その変数は無効になり、ブロック外で宣言されていた変数が有効になります。

大きなプログラムなどでは、意図せずグローバル変数と同じ名前の変数を使ってしまうことがありますが、ブロックと let を使えば、このようなケースが避けられるものと思います。

よって、ブロック内でローカルな変数は let を使って宣言する方が安全です。

サンプル

このサンプルは3つの部分からなっています。「ブロックの振る舞い」は、var と let によって宣言された変数のスコープを確認するためのものです。「var を使用した for 文とブロックの振る舞い」は var で繰り返し変数 i を定義した場合、「let を使用した for 文とブロックの振る舞い」は let で繰り返し変数 i を定義した場合を比較するためのものです。

'use strict';
/* ブロックと var, let のサンプル 2017-11 tadnakam */
/* ブロックの振る舞い */
console.clear();
console.info('-- ブロックの振る舞い --');

var n = 100;
console.log("ブロック開始前 n = %i", n);  // 100

// ブロック
{
  let n = 200;  // この n は、ブロック内でのみ有効。
  var n2 = 400; // n2 はブロック外でも有効。
  console.log("ブロック内 n = %i", n);
}
// ブロック終わり
console.log("ブロックが閉じた後 n = %i", n);  // ブロック外で定義した n の値が表示される。
console.log("ブロックが閉じた後 n2 = %i", n2); // ブロック内で定義した n2 の値が表示される。

/* var を使用した for 文とブロックの振る舞い */
console.log('\n-- var を使用した for 文とブロックの振る舞い --');

// ブロック外で i を定義し 100 を代入。
var i = 100;

console.group('for_loop1');  // console.group() はインデント表示の指定
for (var i = 0; i < 4; i++) {  // i を再定義してみると・・
  console.log(i);
}
console.groupEnd();

console.log(i);  // for 文で再定義した i は値が変化してしまう。

/* let を使用した for 文とブロックの振る舞い */
console.log('\n-- let を使用した for 文とブロックの振る舞い --');
// 前に定義した i にブロック外で 100 を代入。
i = 100;

console.group('for_loop2');
for (let i = 0; i < 4; i++) {  // let で i を再定義するとブロック {} の外でもブロック内ローカルになる。
  console.log(i);
}
console.groupEnd();

// ブロック外で定義した i の値がそのまま残っている。
console.log(i);  // i = 100

実行例

Java や C# との比較

Java や C# にもブロックはありますが、let に相当するものはありません。したがって、ブロックでブロック外で宣言した変数と同じ名前の変数を宣言することはできません。その場合は、コンパイルエラーになるので、誤ってグローバルな変数と同じ名前の変数を使ってしまう恐れはありません。

Java のブロック

public class Test1 {

    public static void main(String[] args) {
        System.out.println("Java ブロックテスト");
        int n = 10;
        System.out.println(n); // 10

        // ブロック
        {
            //int n = 9;  // n はブロック外で定義済みなので、エラーになる。
            int i = 5;  // ブロック内で i を定義。
            System.out.println(n);  // 10
            System.out.println(i);  // 5
            n++;  // ブロック外で定義した n の値を更新。 n = 11 になる。
        }

        System.out.println(n);  // 11
        // System.out.println(i);  // i は未定義なのでエラーとなる。
    }
}

実行例

C# のブロック

using System;

namespace BlockTest
{
    class Program
    {
        /// 

        /// ブロックのテスト
        /// 

        /// 
        static void Main(string[] args)
        {
            int n = 10;
            Console.WriteLine(n);

            // ブロック
            {
                // int n = 100; // n はブロック外で定義済みなのでエラーとなる。
                int i = 5;
                Console.WriteLine(n);  // 10 と表示される。
                Console.WriteLine(i);  // 5 と表示される。
                n++;  // n を使って演算してみる。
            }

            Console.WriteLine(n);  // 11 と表示される。
            // Console.WriteLine(i);  // i は未定義になりエラー。

#if DEBUG
            Console.ReadKey();
#endif
        }
    }
}

実行例

終わり