
【JavaScript入門】巻き上げ
JavaScript の let
変数には、同じスコープ内で同名の変数を宣言するとエラーになるというルールがありますが、もうひとつ注意が必要な点として「巻き上げ」の仕様があります。ブロックの中で同名の変数を新たに宣言すると、たとえ同じ名前であっても、内部のスコープでは別の変数として扱われます。そのために、思わぬタイミングで「未宣言エラー(初期化前にアクセス)」が起きることがあるのです。

1.巻き上げとは
JavaScript の let
変数は、ブロックや関数スコープ内で宣言されると、宣言箇所より上に書かれたコードからは利用できません。しかし、内部的にはスコープの最上部で変数が「確保」されているような動作になり(これを巻き上げ/hoisting と呼ぶことがあります)、宣言より上のコードで参照するとエラーが発生する場合があります。
2.実例:巻き上げによるエラー
以下のコードでは、外側のブロックで item
という変数を宣言しています。その後、内側のブロックでも item
を宣言しているため、内側のブロックの冒頭で console.log(item)
を呼び出すと「初期化前に参照している」というエラーが起きます。
【myHoistingSample.html】
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>myHoistingSample.html</title>
</head>
<body>
<h1>巻き上げのデモ</h1>
<p>ブラウザの開発者ツール(コンソール)を開いて結果を確認してください。</p>
<script>
let isHoliday = true;
if (isHoliday) {
let item = 'スマホ';
console.log('階層1:', item);
if (isHoliday) {
// ここで内側の item が優先され、まだ宣言されていない扱いとなる
console.log(' 階層2:', item);
let item = 'タブレット';
}
}
</script>
</body>
</html>
実行結果

デバッグコンソールの出力
階層1: スマホ
Uncaught ReferenceError ReferenceError: Cannot access 'item' before initialization
上記を実行すると、「階層1: スマホ」という出力はコンソールに表示されますが、その後で console.log(' 階層2:', item);
の部分がエラーを引き起こします。内側のブロックが始まった時点で新たに宣言される let item = 'タブレット'
が「巻き上げ」によって「存在はしているが、まだ初期化前」な状態とみなされ、アクセスできないとみなされるためです。
まとめ
let
変数はスコープの開始部分で定義されているように扱われる(巻き上げ)が、実際に初期化されるのは宣言文に到達したときになります。そのため、宣言より前でアクセスすると「初期化されていない」というエラーになる。- 外側のスコープと内側のスコープで同名の変数を宣言した場合、内側の変数が優先して評価されます(シャドーイング)。外側で宣言したものが即座に無効になるわけではありませんが、内側の変数が存在する範囲内ではそちらが使われます。
- それほど頻繁に問題になるわけではありませんが、大規模なコードや複雑な入れ子がある場合、知らないと「なぜ動かないのかわからない」事態に陥ることがあります。
こうした仕様があると理解しておくことで、宣言前に変数を参照しようとしてハマるバグを回避できます。JavaScript でブロックスコープを扱うときは、巻き上げの振る舞いを念頭に置き、変数は使う直前ではなく「スコープの先頭付近」で宣言するなどの工夫をすると安全です。