JavaScriptにおける変数の初期化と変数宣言とスコープについて


# 変数の初期化
JavaScriptでは、変数は使用する前に必ず宣言する必要があります。
もし宣言せずに変数を使おうとすると、JavaScriptは `ReferenceError: x is not defined`というようなエラーを返します。

```javascript: 変数 x は初期化されていないためundefinedを返す
console.log(x); // ReferenceError: x is not defined
```

## 変数の宣言と初期化の例
変数を使用する前に、必ず変数の初期化と変数宣言をしましょう。
という事で、letを使った例を見ていきます。
```javascript: 変数 a は宣言されているが、値は未定義(undefined)
let a; => undefined
console.log(a); // => undefined
console.log(Boolean(a)); // => false
```
```javascript: 変数 b にはnullという値が代入されている
let b = null; // => undefined（nullという値が代入されている）
console.log(b); // => null
console.log(Boolean(b)); // => false
```
```javascript: 変数 c には空の文字列が代入されている
let c = ''; // => ''
console.log(c); // => ''
console.log(Boolean(c)); // => false
```
```javascript: 変数 d は 'hoge' という値で初期化されている
let d = 'hoge'; // => undefined（hogeという値が代入されている）
console.log(d); // => 'hoge'
console.log(Boolean(d)); // => true
```

#### 解説：
一通りの変数の宣言と初期化の説明とコード例を見てみました。
それぞれの例を実行してみると、変数の宣言の仕方によって出力結果が異なる事が理解できます。

# 変数の宣言
JavaScriptには主に3つの変数宣言があります。
それぞれの変数宣言とスコープ（変数が有効である範囲）と特性について説明します。

## var、let、constはJavaScriptの「変数の宣言方法」
それぞれが異なる「スコープ」（変数が参照可能な範囲）と「変数の再割り当てのルール」（変数に新たな値を代入できるかどうか）を持っています。

## 簡単に説明すると
#### ・ varは「関数スコープ」を持つ変数を宣言する
#### ・ letとconstは「ブロックスコープ」を持つ変数を宣言する
#### ・ constは再代入が不可能な変数（定数）を宣言する

このように理解すると、それぞれの変数宣言がどのような状況で使用すべきか、コードを読む際に何を期待するべきかが明確になります。

# 変数宣言の種類とスコープ
JavaScriptのスコープは以下の２つのみです。

* グローバルスコープ
* ローカルスコープ

以下の表は、変数の宣言方法と各スコープ内での挙動を示しています。
これらを踏まえて次項に進みます。

|変数宣言方法|グローバルスコープ|関数スコープ  |ブロックスコープ|
|:---------|:-----------|:------|:-------|
|var       |グローバル    |ローカル|グローバル|
|let       |グローバル    |ローカル|ローカル|
|const     |グローバル    |ローカル|ローカル|
|変数名のみ  |グローバル    |グローバル|グローバル|


## 1. var（再代入可）※ 非推奨
以下がvarの性質です。

* グローバル領域内での宣言
他の宣言と同様に、グローバルスコープで利用されます。

* ブロック内での宣言
ブロック内でvarを宣言した場合でも、グローバルに利用されます。

* 関数内での宣言 （ここ注目！）
  * 関数内でvarを使用して変数を宣言した場合、その変数はその関数内およびその関数内で定義されたブロックスコープ内でのみ利用可能です
  * また、その変数は宣言された関数より上位のスコープでは利用できません。

#### 総括
<font color="red">varで宣言された変数はスコープの制限が緩いため、意図しない挙動を招く可能性があります。したがって、日常的なコーディングではletやconstの使用が推奨されます。</font>
ただし、既存のコードを理解する上でvarの挙動を知っておくことは重要です。

### 非推奨である理由

### 1 - 変数スコープの問題
varには変数のスコープ（範囲）の問題があります。
`var`と`let`で比較してみます。
```javascript: ❌ 変数のスコープ（var）：if文内で定義した変数が漏れている
function hoge() {
  if (true) {
    var x = 10;
    console.log("if文内の: " + x); // => "if文内の: 10"
  }
  console.log("hoge関数内の: " + x); // => "hoge関数内の: 10"
}
hoge();
```
#### 解説：varのスコープ
上記の例では、if文のブロック内で宣言された変数xが、
hoge関数スコープ内でも利用できてしまっています。
<font color="red"><b>これは意図しないスコープの漏れや衝突を引き起こす可能性があります。</b></font>


次に、let,constを見てみましょう。
```javascript: ⭕️ 変数のスコープ（let,const）：if文内で定義した変数が漏れない
function hoge() {
  if (true) {
    let x = 10;
    console.log("if文内の: " + x); // => "if文内の: 10"
  }
  console.log("hoge関数内の: " + x); // => x is not defined!
}
hoge();
```
#### 解説：let,constのスコープ
letやconstはブロックスコープを持ちます。
そのため、if文の様なブロックスコープ内に制限して変数を利用できます。
<font color="red"><b>より正確なスコープ管理</b></font>ができているのがわかります。

### 2 - varで宣言された変数は再宣言が許容される

```javascript: ❌ 変数再宣言を許容する（var）
// 変数の再宣言が通ってしまう
var x = 10;
var x = 100; // エラーが発生せず、100に上書きされる
console.log(x); // => 100
```
```javascript: ⭕️ 変数再宣言を許容しない（let,const）
// 変数の再宣言はエラーとなる
let y = 20;
let y = 200; // この時点で、エラーになっている
console.log(y); // => SyntaxError: Identifier 'y' has already been declared
```
#### 解説：
上記の例では変数xが2度宣言されていますが、エラーが発生せずに2回目の宣言が有効となっています。

<font color="red"><b>これは変数の意図しない上書きや混乱を引き起こす可能性があります</b></font>

letやconstは同じスコープ内での再宣言がエラーとなるため、変数の重複宣言を防ぐことができます。

### 3 - 変数の巻き上げ（Hoisting）
変数宣言が行われる前に、変数を使用したらどうなるでしょうか？
varとletで比較してみます。
```javascript: ❌ varを使用した変数宣言の巻き上げ
console.log(name); // => undefined（未定義を示す"値"）
var name = "Hoge";
console.log(name); // => Hoge
```
#### 解説：var
上記の例では、変数nameの宣言がconsole.log(name)よりも前にあるにも関わらず、エラーが発生せず`undefined（値あり）`が出力されます。
これは、varは変数宣言が巻き上げられているためです。

```javascript: ⭕️ let(またはconst)を使用した変数宣言の巻き上げ
console.log(name); // => ReferenceError: name is not defined（値がない）
let name = "Hoge";
console.log(name); // => Hoge
```
#### 解説：let,const
上記の例では、letを使用した変数nameの宣言が巻き上げられません。
そのため、宣言前に変数を使用しようとすると`ReferenceError（値なし）`が発生します。

## 2. let（再代入可）
letで宣言された変数はブロックスコープを持ちます。
そのため、letで宣言された変数は、その宣言があるブロック（{}で囲まれた範囲）内でのみ利用可能です。また、同一スコープ内でletによって同じ名前の変数を再宣言するとエラーになります。

```javascript: let
if (true) {
  let y = "ブロック内の宣言";
  console.log(y);  // "ブロック内の宣言"
}
console.log(y);  // ReferenceError: y is not defined
```


## 3. const（再代入不可）
constもlet同様にブロックスコープを持つ変数宣言ですが、一度値が代入された後は再代入できないという特性があります。
つまり、constで宣言された変数は、一種の定数として扱われます。

しかし、<font color="red">オブジェクトや配列の場合はそのプロパティや要素自体の変更は可能で、再代入不可という制約はその変数の参照自体にのみ適用</font>されます。

``` javascript: const
const z = "定数の宣言";
console.log(z);  // "定数の宣言"
z = "値を代入";  // TypeError: Assignment to constant variable.
```
このように、JavaScriptの各種変数宣言にはそれぞれ異なるスコープと特性があり、適切な場所で適切な宣言を使うことで、プログラムのバグを防ぎ可読性を高めることができます。

# まとめ
考え方としてはこのようなイメージでしょうか。
- <font color="red">varの使用はダメゼッタイ！</font>
- 基本的に不変の値としてconstを使う
- 必要に応じて再割り当てが必要な場合のみletを使う

上記が最新のJavaScriptコーディングのベストプラクティスです。
特に、constの使用はコードの予見性を向上させ、バグを防ぐのに役立ちます。