変数の初期化
JavaScriptでは、変数は使用する前に必ず宣言する必要があります。
もし宣言せずに変数を使おうとすると、JavaScriptは ReferenceError: x is not defined
というようなエラーを返します。
console.log(x); // ReferenceError: x is not defined
変数の宣言と初期化の例
変数を使用する前に、必ず変数の初期化と変数宣言をしましょう。
という事で、letを使った例を見ていきます。
let a; => undefined
console.log(a); // => undefined
console.log(Boolean(a)); // => false
let b = null; // => undefined(nullという値が代入されている)
console.log(b); // => null
console.log(Boolean(b)); // => false
let c = ''; // => ''
console.log(c); // => ''
console.log(Boolean(c)); // => false
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のスコープは以下の2つのみです。
- グローバルスコープ
- ローカルスコープ
以下の表は、変数の宣言方法と各スコープ内での挙動を示しています。
これらを踏まえて次項に進みます。
変数宣言方法 | グローバルスコープ | 関数スコープ | ブロックスコープ |
---|---|---|---|
var | グローバル | ローカル | グローバル |
let | グローバル | ローカル | ローカル |
const | グローバル | ローカル | ローカル |
変数名のみ | グローバル | グローバル | グローバル |
1. var(再代入可)※ 非推奨
以下がvarの性質です。
-
グローバル領域内での宣言
他の宣言と同様に、グローバルスコープで利用されます。 -
ブロック内での宣言
ブロック内でvarを宣言した場合でも、グローバルに利用されます。 -
関数内での宣言 (ここ注目!)
- 関数内でvarを使用して変数を宣言した場合、その変数はその関数内およびその関数内で定義されたブロックスコープ内でのみ利用可能です
- また、その変数は宣言された関数より上位のスコープでは利用できません。
総括
varで宣言された変数はスコープの制限が緩いため、意図しない挙動を招く可能性があります。したがって、日常的なコーディングではletやconstの使用が推奨されます。
ただし、既存のコードを理解する上でvarの挙動を知っておくことは重要です。
非推奨である理由
1 - 変数スコープの問題
varには変数のスコープ(範囲)の問題があります。
var
とlet
で比較してみます。
function hoge() {
if (true) {
var x = 10;
console.log("if文内の: " + x); // => "if文内の: 10"
}
console.log("hoge関数内の: " + x); // => "hoge関数内の: 10"
}
hoge();
解説:varのスコープ
上記の例では、if文のブロック内で宣言された変数xが、
hoge関数スコープ内でも利用できてしまっています。
これは意図しないスコープの漏れや衝突を引き起こす可能性があります。
次に、let,constを見てみましょう。
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文の様なブロックスコープ内に制限して変数を利用できます。
より正確なスコープ管理ができているのがわかります。
2 - varで宣言された変数は再宣言が許容される
// 変数の再宣言が通ってしまう
var x = 10;
var x = 100; // エラーが発生せず、100に上書きされる
console.log(x); // => 100
// 変数の再宣言はエラーとなる
let y = 20;
let y = 200; // この時点で、エラーになっている
console.log(y); // => SyntaxError: Identifier 'y' has already been declared
解説:
上記の例では変数xが2度宣言されていますが、エラーが発生せずに2回目の宣言が有効となっています。
これは変数の意図しない上書きや混乱を引き起こす可能性があります
letやconstは同じスコープ内での再宣言がエラーとなるため、変数の重複宣言を防ぐことができます。
3 - 変数の巻き上げ(Hoisting)
変数宣言が行われる前に、変数を使用したらどうなるでしょうか?
varとletで比較してみます。
console.log(name); // => undefined(未定義を示す"値")
var name = "Hoge";
console.log(name); // => Hoge
解説:var
上記の例では、変数nameの宣言がconsole.log(name)よりも前にあるにも関わらず、エラーが発生せずundefined(値あり)
が出力されます。
これは、varは変数宣言が巻き上げられているためです。
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によって同じ名前の変数を再宣言するとエラーになります。
if (true) {
let y = "ブロック内の宣言";
console.log(y); // "ブロック内の宣言"
}
console.log(y); // ReferenceError: y is not defined
3. const(再代入不可)
constもlet同様にブロックスコープを持つ変数宣言ですが、一度値が代入された後は再代入できないという特性があります。
つまり、constで宣言された変数は、一種の定数として扱われます。
しかし、オブジェクトや配列の場合はそのプロパティや要素自体の変更は可能で、再代入不可という制約はその変数の参照自体にのみ適用されます。
const z = "定数の宣言";
console.log(z); // "定数の宣言"
z = "値を代入"; // TypeError: Assignment to constant variable.
このように、JavaScriptの各種変数宣言にはそれぞれ異なるスコープと特性があり、適切な場所で適切な宣言を使うことで、プログラムのバグを防ぎ可読性を高めることができます。
まとめ
考え方としてはこのようなイメージでしょうか。
- varの使用はダメゼッタイ!
- 基本的に不変の値としてconstを使う
- 必要に応じて再割り当てが必要な場合のみletを使う
上記が最新のJavaScriptコーディングのベストプラクティスです。
特に、constの使用はコードの予見性を向上させ、バグを防ぐのに役立ちます。