var と let/const の違い（＋ 関数とホイスティングのお話）

<h2>前提</h2>
８月末から、プログラミングの学習を始めました。
学習の質を高めるため、学んだことを Qiita にアウトプットする取り組みを続けています。

現在は、HTML/CSS の学習がひと段落ついたので、JavaScript の学習に入りました。
ただ、JavaScript に入ってから、学習内容がかなり本格化してきたのを感じています。
学んだことをかみ砕くのに時間がかかるので、Qiita へのアウトプットも滞りがちになってしまいました。

これじゃいかん！
ということで、急ぎ投稿したのがこの記事です。

<h2>この記事の目的</h2>
ここ数日でようやく腑に落ちてきた「ホイスティング関連」について、自分なりにまとめるのが目的です。
アップアップしながら書いているので、思わぬ間違いがあるかもしれません。
（余裕がないあまり、本文は「ですます調」ですらありません・汗）

もしお読みくださった方が間違いに気づかれましたら、ご指摘いただけると大変助かります。

それでは以下、本文に移ります。

<h2>宣言文の前で呼び出せるか？ その1</h2>

まずは、宣言文の前に console.log を置いた例で考えてみる。

<h3>var の場合</h3>

```javascript:JavaScript

console.log(num);

var num=1;
```
この場合、console には<strong>「undefined」</strong>と出力される。

ここでの「undefined」とは、
<b>まだ具体的な数値の入っていない「空き箱」</b>
のようなイメージで考えればよい。

つまり、
　　　① var 宣言文 によって「num の空き箱」が作られる　　　　　
　　　② この「空き箱」が埋まるのは、宣言文よりも後ろ

すると、「console.log(num);」は宣言文の前なので、そこで使われる num はまだ「空き箱」。
そのため、console には「undefined」と出力された。


<h3>let の場合</h3>

```javascript:JavaScript

console.log(num);

let num=1;
```

この場合、console は<b>エラー</b>を出力する。

なぜなら、let は var と異なり、<b>空き箱すら作らない。</b>
そのため、宣言文よりも前では、<b>「num など存在しない」</b>ということになる。

したがって、「console.log(num);」はエラーを返すほかない。

<h3>const の場合</h3>

```javascript:JavaScript

console.log(num);

const num=1;
```
let と同じく、console は<b>エラー</b>を出力する。

その理由も、let と同じ。

<h3>ここまでのまとめ</h3>

undefined だろうがエラーだろうが、うまく動いていないのは同じじゃない？
わざわざ var を封印しなくてもいいんんじゃない？

・・・とも思える。
だが、次の例を見れば<b>「 var が非推奨になる理由」をより明確に実感できる。</b>

<h2>宣言文の前で呼び出せるか？ その2</h2>

次は、console.log よりも前に「num=500;」が書かれている例で考えてみる。

<h3>var の場合</h3>

```javascript:JavaScript

num=500;

console.log(num);

var num=1;
```

この場合、console には<strong>「500」</strong>と出力される。

というのも、var によって作られた「num の空き箱」は、宣言文の前後を問わず使える。
「num=500;」の位置でも、<b>「num の空き箱」はれっきと存在している</b>のだ。

その結果「num=500;」が、「num の空き箱」の中に 500 を入れる。
この 500 は、そのまま「console.log(num);」によって出力されることになる。

・・・これの何がまずいのだろう？

上記の例では、「num=500;」は宣言文からとても近い場所にある。
そのため、それほど問題はないようにも思える。

しかし仮に「num=500;」が、<b>宣言文から10,000行離れた場所にうっかり残した消し忘れ</b>だったとしたらどうだろう？
var で宣言した変数は、<b>コード内のいかなる場所からでも、出力結果に影響を与えることができてしまうのだ。</b>

そう考えると、<b>var の使用がバグの温床になりかねない</b>ことを実感できる。

<h3>let の場合</h3>

```javascript:JavaScript

num=500;

console.log(num);

let num=1;
```

この場合、console には<strong>エラー</strong>が出力される。

なぜなら、let を用いた変数宣言では、「空き箱」が作られない。
宣言文よりも前の場所では、<b>「num という箱じたいが存在しない」</b>ことになる。

したがって、宣言文より先に「num=500;」と書いてあったとしても、
<b>「num って何よ？？」</b>
ということになる。
その結果、console.log はエラーを返すほかない。

このように let は、<b>バグの温床になりかねない場面で、明確にエラーを返してくれる。</b>
その意味で、var よりも let を使うべきなのだ。

<h3>const の場合</h3>

```javascript:JavaScript

num=500;

console.log(num);

const num=1;
```

let を用いた場合と同じく、console には<strong>エラー</strong>が出力される。

この場面では、const の挙動は let とまったく同じだ。

<b>const との関係でも、 var はやはり非推奨</b>ということになる。 

<h3>ここまでのまとめ</h3>

基本的に、
「var は使わず、let / const を使うべき」
ということがわかった。

なぜなら、<b>var で宣言した変数には、コードのどこからでも代入できてしまうから。</b>
変数の予期せぬ挙動を防ぐためには、let / const を使うのが安全だということになる。

<h3>ちなみに、let / const の使い分けは？</h3>

なお、let と const の違いは「再代入の可否」。
<b>let は再代入可能、const は再代入不可能</b>、という違いがある。

たとえば、console に「0」「1」「2」と連続して出力されるループ処理を行いたいとする。
この場合、次のようなコードを使う。

```javascript:JavaScript

for(let i=0; i<3; i=i+1){
  console.log(i);
}

```

このfor 文の中には、「i=i+1」という「変数 i への再代入」の記述がある。
つまり、これは「再代入が必要なコード」である。

こうしたコードを書くには、let を使わなければならない。
const を使うと再代入ができなくなるからだ。

言い換えると、<b>再代入の不要なコードで let を使うと、「再代入できてしまうことによるバグ」が発生するおそれがある。</b>

結論としては、次のような方針が推奨される。
　① 基本的には const を使う
　② 再代入が必要な場合のみ let を使う


<h2>おまけ: 関数とホイスティングのお話（※宣言文との位置関係に関して）</h2>

これまで「宣言文との位置関係」を中心に、var / let / const について考えてきた。

こうした「宣言文との位置関係」の話は、ホイスティングとも呼ばれる。
ホスティングとは、<b>宣言された変数・関数が、宣言文よりも前の位置で実行される挙動</b>のこと。
宣言文よりも前に「巻き上げること(=hoisting)」が、ホイスティングという名前の由来。

つまりこれまでは、var / let / const を題材に、変数のホイスティングについて見てきたともいえる。
そこで最後に、<b>関数オブジェクトのホイスティング</b>についても触れてみる。

たとえば、次のようなシンプルな（シンプルすぎる）関数を考える。

```javascript:JavaScript

function A(){
  const num=1;
  console.log(num);
}

A();
```
もちろん、コードの最後に書かれた「A();」の記述によって関数A が実行され、console には「1」と出力される。

では、このコードを次のように書き換えるとどうなるか。



```javascript:JavaScript

A();

function A(){
  const num=1;
  console.log(num);
}
```
さきほどは最後に書かれていた「A();」が、今度は先頭に書かれている。
こうした記述でも、関数A は問題なく実行され、console には「1」と出力される。

このように関数オブジェクトは、関数宣言より前に実行文を置いても、なんら問題なく挙動する。
言い換えると、<b>宣言文よりも前に巻き上げて（ホイスティングして）実行することができる。</b>

ホイスティング可能な var が非推奨になった経緯を考えると、関数オブジェクトがホイスティング可能というのは、むしろ注意すべき点なのかもしれない。

今後も注意して、引き続き学習を進めていきたい。
<hr>
・・・今回は、以上です。
最後までお読みくださり、ありがとうございました！