Javascriptのvar、let、constの違いを調べてみた

プログラミングには変数がつきものです。
各言語によって宣言方法は異なりますが、Javascriptの場合の変数宣言は長らく「var」だけでOKでした。
ところが最近、「var」以外に「let」と「const」という変数宣言が増えました。

いったい何が違うの?

と疑問に思ったため、いつものごとく調べてみました。

ざっくりと変数の違いを表にしてみた。

var、let、constの違いについて調べると、次のような表がでてきました。

varletconst
(1)再代入可能可能不可
(2)再宣言可能不可不可
(3)スコープ関数スコープブロックスコープブロックスコープ
(4)ホイスティングされるされる(TDZあり)される(TDZあり)

表だけ見るとわかったような気になりますが、私はさっぱりわかりませんでした。
再代入や、再宣言は何となくイメージが付きますが、スコープ、ホイスティングっていったい何?

なんだか余計にわからなくなってきました。
ということで一つずつ調べてみました。

(1)再代入の違い

再代入とは、変数の中身に新たな内容をセットすることです。
具体的には以下のような内容です。

var a = 0;
a = 1;

変数を宣言し、初期値を入れた後に違う値をセットすること。
それを再代入といいます。

varとletは再代入OKですが、constは再代入できません。
次のようになります。

var a = 1;
let b = 1;
const c = 1;

a = 2; // var 変数は再代入OK
b = 2; // let 変数は再代入OK
c = 2; // TypeError const 変数は再代入すると致命的エラーになります

constは1宣言に対し、1回しか値をセットできないようです。
初期値などによいのかもしれません。

(2)再宣言の違い

再宣言とは、宣言した後に再び宣言することです。
具体的には以下のようなことです。

var a = 0;
var a = 1;

あまり意味のないことですが、宣言を2回しています。
varは再宣言はできますが、letとconstは再宣言するとエラーになります。

次のようになります。

var a = 0;
let b = 0;
const c = 0;

var a = 1; // var の再宣言はOK
let b = 1; // SyntaxError let の再宣言は文法エラー
const c = 1; // SyntaxError const の再宣言は文法エラー

(3)スコープの違い

スコープの違いといわれてもよくわかりません。
スコープとは、変数の影響範囲を示します。
一般的には外部変数、内部変数というイメージですが、Javascriptの場合もっと細かく定義されています。

意味合い的には以下のような意味になります。

sample();

function sample()
{
    for(count=0 ; count < 3 ; count++)
    {
        var a = count;
    }
console.log(a); // var の場合同一関数内であれば参照できる
}

console.log(a); // 関数の外だとエラーになる。

var は内部変数、外部変数的な動作をしますが、let constの場合は少し違います。

sample();

function sample()
{
for(count=0 ; count < 3 ; count++)
{
var a = count;
let b = count;
const c = count;
}
console.log(a); // var の場合同一関数内であれば参照できる
console.log(b); // ReferenceError let の場合階層が異なるとエラーになる。
console.log(c); // ReferenceError const の場合階層が異なるとエラーになる。

}

console.log(a); // ReferenceError var の場合関数の外だとエラーになる。
console.log(b); // ReferenceError let の場合関数の外もとエラーになる。
console.log(c); // ReferenceError const の場合階関数の外もとエラーになる。

letとconstは、同一関数内であっても、同一階層より上位だとエラーになります。
下位階層だとエラーになりません。

ここで疑問:forの中の const c = count;は再宣言にならないの?

サンプルを書いていて疑問に感じたことがあります。
constは、再代入や再宣言は文法エラーになります。
なのに、forでループしている中に毎回値をセットしてよいのでしょうか?

調べてみると、forの{}内はループごとに新しいスコープ(ブロック)が生成されるため、その都度新規宣言していることになります。constは、別スコープ(ブロック)であれば、重複宣言にならないため、ループ処理内で、毎回値をセットしても大丈夫でした。

ただし、以下のような書き方だと致命的エラーになります。

function sample()
{
    const a = 0;
    for(cnt=0 ; cnt < 3 ; cnt++)
    {
	a = cnt;
    }
}

これはconstの宣言1回に対して、const変数への代入を何度も行っているためです。
varやletならばエラーにならないです。

ループの中で宣言はOKなのに、なんとも不思議な仕様です。

(4)ホイスティングの違い

ホイスティングって何?
調べれば調べるほど意味が分かりませんでした。

実際には使うことはほどんどないと思われますが、varとの違いという意味では重要な意味があります。

console.log(a);
var a = 1;
// aは後から宣言されていますが、記述上文法エラーになりません。

この例では、宣言する前に変数を使用しています。
ホイスティングとは、宣言する前に使用することです。
変数の場合、ほとんど意味がないですが、関数の場合だと重要な意味があります。

例えば以下のケースはよくあると思います。

sample();

function sample()
{
    関数の中身
}

関数宣言する前に、関数をコールする。
プログラムではよくあることです。
ですが宣言前にエラーにならないのは、ホイスティングしているからと言えます。

では、letとconstの場合はどうなるのでしょうか?
それがこちらです。

console.log(a); // var の場合エラーにならない。
var a;
console.log(a); // 宣言後はエラーにならない

console.log(b); // let の場合ReferenceErrorになる。
let b;
console.log(b); // 宣言後はエラーにならない

console.log(c); // constの場合SyntaxErrorになる。
const c; // constの場合初期化をしない宣言だけの場合はエラーになる。
console.log(c); // 宣言自体がエラーなので宣言後もエラー

宣言前に変数を参照する場合、var以外はエラーになります。
ですが、letとconstでエラーの毛色が異なります。

letはReferenceErrorになるのに、constはSyntaxErrorです。
宣言前に変数を参照すると、なぜこのような違いが出るのでしょうか?

letの場合はTDZエラー

TDZってよく意味の分からない言葉ですが、変数の宣言が終わるまでの期間というような意味です。
ホイスティング(宣言前の参照)をすると、TDZ内アクセス(変数の宣言が終わる前のアクセス)となります。

letがReferenceErrorになるのは、変数宣言が終わる前に変数参照をしているからです。
varの場合はエラーになりませんが、letの場合だとエラーになります。

ちなみに以下の場合はエラーになりません。
先に宣言しているため、TDZ後アクセスになるためです。

let b;
console.log(b);

constの場合は初期化エラー

letの場合はTDZによるエラーでしたが、const変数の場合は、SyntaxError(文法エラー)になります。
これは、const変数の場合は初期化が必須なためです。

つまり以下のように、宣言と同時に初期化をすればOKです。

const c;
console.log(c);

各変数のまとめ

各変数をまとめると次のようになります。

  • varは何でもかんでもOK
  • 普段使うならletがよい
  • constは初期値(設定値)指定用

Jqueryで使う場合はどうなるのか?

実際に使用する場合、Jqueryを交えて使うことがほとんどです。
ということで、Jqueryの書式で検証してみました。

var _a;
let _b;
const _c=3;

$(function()
{
_a = 10;
_b = 20;
_c = 30; // TypeErrorになる。(再代入によるエラー)
sample();
sample02();

});

function sample()
{
// 関数外で宣言した変数の参照はOK
console.log(_a);
console.log(_b);
console.log(_c);
}

function sample02()
{
// 関数内で再宣言するのは、スコープが異なるためOK
var _a = “text1";
let _b = “text2";
const _c = “text3";

console.log(_a);
console.log(_b);
console.log(_c);
}

最上位で宣言した変数は、引数渡しをしなくても下層まで参照できました。
意外と、varをletに書き換えるだけでよさそうです。

結局のところ正しく動けばOKだけどlet constを使えばより良くなる。

Javascriptにletやconstという変数指定が増えたのは、意図しない記述ミスをエラーとするためです。
どういうことかというと、すでに使っている変数を忘れて、別の意味で同じ名称の変数を宣言してしまうと、論理的エラー(バグ)がかなり高い確率で発生します。

これを明示的にわかりやすくするために生み出されたようです。

変数名の管理や、論理エラーを精密に管理できるならば、varのままで問題ありません。
ですが、letやconstを使えば、論理エラーをさらに減らせるということになります。

結局のところ、不具合なく動作させられば、変数の型は何でもよいのです。
ですが、letやconstを使えば、うっかりミスを減らせるため、より良くなります。

今までletやconstって何だろう?
と思っていましたが、知ることでより向上できました。

今回のレポートは以上です。
読んでいただいてありがとうございました。


ホームページに関するお悩み事やご相談事がございましたら私どもまでご連絡ください。 鋭意ご対応申し上げます。
ホームページのご提案もさせていただいております