コレを読めば理解できる！const、let、varの違い

# 読み飛ばしてください

おはようございます、しなもんです。
先日高校で情報のテストがあったのですが、**無事にJavascriptの問題**で詰みました。
（const使っとけばいいと思ってたら性質を利用する問題が出て詰んだ）
~~Pythonばっかり書いてる弊害がここに...~~

ので、この記事では**改めて`const`, `let`, `var`の違いをまとめたいと思います。**

# そもそもなんで種類が分かれてるんだ？
そもそも、`const`、`let`、`var`というものが別々に存在する理由はなんなのでしょうか？

Javascriptでは初期、`var`しか存在しませんでした。
そのため、**変数のスコープや再宣言ルールが曖昧で**、
**予期せぬバグが発生しやすい**という問題がありました。

`const`と`let`は、他の多くのプログラミング言語でも採用されている変数宣言方法であり、
Javascriptでもこれを取り入れることで、より信頼性の高い言語へ進化できたのです。

つまりは**意図しないバグを防ぐために変数宣言時に制限を設けた**のです。

# コレ読めばとりあえず理解る？名前の由来
じゃあ名前の由来はなんなのでしょうか？

それぞれの名前は、以下の意味を英語で表しています。

- const (constant): 不変、定数
- let (let): 許す、させる
- var (variable): 変数

`const`は、値を変更できない不変変数を宣言するために使用されるため、
**不変**を意味するそのままの名が付けられました。

`let`は、ブロックスコープ変数を宣言し、その変数に値を代入することを可能にするため、
変数に値を代入することを**許す**、値を**与える**から`let`と命名されたと考えられます。

`var`は、変数を宣言するために使用される汎用的なキーワードであり、
歴史的な経緯からこの名が付けられています。一番ありふれたものです。

# 改めてそれぞれを知ってみよう

### `const`
- **再宣言**も**再代入**も不可です。
- すでに宣言済みの変数名で再度宣言しようとすると、エラーが発生します。
- 宣言時に必ず値を初期化する必要があります。

言い換えると、
- 同じ名前で変数を作りなおすことができない（再宣言不可）
- 一度値を入れたら入れなおすことができない（再代入不可）
- 変数を作るときには必ず値を入れなければいけない

例えば、
```javascript
const PI = 3.14159;
```
と定義した後に
```javascript
PI = 3.14159; // エラー: 'PI' is already declared
```
もう一度代入しようとすると普通にエラーを吐きます。

### `let`
- **再代入**は可能ですが、**再宣言**は不可です。
- 同じスコープ内で同じ変数名を再度宣言しようとすると、エラーが発生します。
- 宣言時に初期化しても初期化しなくても構いません。

これも言い換えると、
- 同じ名前で変数を作りなおすことができない（再宣言不可）
- 値を何度でも入れなおすことができる（再代入）
- 変数を作るときに必ず値を入れる必要がない

つまり、
```javascript
let count;
```
とりあえず変数だけ用意する **←可能**
```javascript
count = 10;
```
後から値を代入する **←可能（上書きもできる）**
```javascript
let count = 20; // エラー: 'count' is already declared
```
**ただし同じ変数を作ることはできない**です。

### `var`
- **再宣言**と**再代入**は両方とも可能です。
- 同じスコープ内で同じ変数名を再度宣言しても、新しい宣言が古い宣言を置き換えます。
- 宣言時に初期化しても初期化しなくても構いません。

要するに
- 同じ名前で変数を作ってもいい（再宣言）
- 値を何回でも入なおしていい（再代入）
- 変数を作るときに必ず値を入れる必要がない

```javascript
var message; // 変数を作るときに必ず値を入れる必要がない
message = 'World'; // 値を上書きできる
var message = 'JavaScript'; // 同じ名前で変数を作ってもいい
```

# それぞれの影響範囲は違う

### `const`と`let`は**ブロックスコープ**を持ちます。

ブロックとは、`{}`で囲まれたコードのことです。
if 文、for 文、while 文などのループ、関数の中などがブロックにあたります。
変数は宣言されたブロック内でのみ有効です。
ブロックの外からはその変数にアクセスできません。

### `var`は**関数スコープ**を持ちます。
関数の中で宣言された変数は、その関数内でのみ有効です。
関数外からはその変数にアクセスできません。
そのため、同じ名前の変数を別の関数で宣言しても、互いに干渉しません。

```javascript
// const
if (true) {
  const num = 100;
  console.log(num); // 100が出力される
}
console.log(num); // エラー: 'num' is not defined

// let
if (true) {
  let num = 200;
  console.log(num); // 200が出力される
}
console.log(num); // エラー: 'num' is not defined

// var
function func1() {
  var message = 'Hello';
  console.log(message); // 'Hello'が出力される
}
function func2() {
  var message = 'World';
  console.log(message); // 'World'が出力される
}
func1();
func2();
```

## ブロック内でも関数内でもない外側で定義されたら？

:::note info
先述した情報のテストで、私はここで詰みました。
上に書いたことは分かっていても、一番外側で定義されたらどうなるか覚えていませんでした...
:::

### コードの一番外側に変数が宣言された場合、その変数は**グローバルスコープ**を持ちます。

プログラムのどこからでもその変数にアクセスできます。
関数の中やブロックの中であっても、その変数を参照することができます。
同じ名前の変数が別のスコープで宣言されていても、グローバル変数が優先的に参照されます。

```javascript
// グローバル変数宣言
var message = 'Hello, World!';

function func1() {
  console.log(message); // 'Hello, World!'が出力される
}

function func2() {
  var message = 'JavaScript'; // 別の変数
  console.log(message); // 'JavaScript'が出力される
}

func1();
func2();
console.log(message); // 'Hello, World!'が出力される
```
覚えておきましょう。

# 巻き上げってなんぞや？

### `const`と`let`は巻き上げが発生しません。
変数の宣言が、実際にその変数を使用する場所よりも**後に書かれていても**、問題なく動作します。

### `var`は巻き上げが発生します。
変数の宣言が、実際にその変数を使用する場所よりも**後に書かれている場合**、その変数は **`undefined`** 値になります。

```javascript
// const
console.log(num); // エラー: 'num' is not defined
const num = 100;

// let
console.log(num); // エラー: 'num' is not defined
let num = 200;

// var
var num = 100;
console.log(num); // 100が出力される
```

# まとめ

これはずっと言われているのですが、基本的に`const`を使用するように心がけましょう。
変数の値が変わらないことが確実な場合は、`const`で宣言することで、
コードの意図が明確になり、後々の修正も楽になります。

また、関数やブロックの外で変数を定義する際も、変数がグローバル変数になってしまうため、
影響が大きくならないよう気を付けるべきだと言われています。

では。