let / const / class を、宣言より前で使ったときに出ます。
console.log(value);
let value = 1;直し方は単純です。宣言を、使うところより前に移します。
なぜ「宣言していない」ではないのか
エラー文が value is not defined ではないことに注目してください。
処理系は value の存在を知っています。 ただ、まだ使える状態になっていないだけです。
let と const は、ブロックの先頭から宣言の行までのあいだ、
一時的死角(TDZ)と呼ばれる状態に置かれます。
名前はあるのに触れない区間です。
var は違います。
notYet();
const notYet = () => {};var は宣言が先頭に巻き上げられ、値は undefined で始まります。
だからエラーにならずに undefined が出ます。
| 宣言より前で使うと | |
|---|---|
var |
undefined(気づけない) |
let / const |
エラーになる |
class |
エラーになる |
| 関数宣言 | 使える |
エラーになるほうが安全です。 var は間違いを黙って通します。
typeof でも避けられない
未宣言の変数なら typeof は安全ですが、一時的死角の中では違います。
new Thing();
class Thing {}const name = '外側';
function show() {
console.log(name);
const name = '内側';
}
show();typeof で存在を確かめる書き方は、ここでは効きません。
原因1: 関数宣言と関数式を混同している
function で書いた関数は、丸ごと巻き上げられるので先に呼べます。
ところが const に入れた関数は呼べません。
同じ「関数」でも書き方で挙動が違います。 アロー関数や関数式を使うなら、定義を呼び出しより上に置いてください。
原因2: クラスを定義より前で使う
class も let と同じ扱いです。巻き上がりません。
原因3: 外側の同じ名前を隠している
これがいちばん見つけにくい形です。
console.log(name) は外側の name を見ていません。
関数の中に const name があるので、その関数の中では最初から内側の name です。
そしてその時点では、まだ触れません。
外側を使いたいなら、内側の宣言の名前を変えてください。
ブロックの中でも同じことが起きます。
似ているエラーとの違い
| 文言 | 意味 |
|---|---|
Cannot access 'x' before initialization |
宣言はある。まだ使えない |
x is not defined |
そもそも宣言が無い(綴り違い・読み込み忘れ) |
Identifier 'x' has already been declared |
同じ名前を2回宣言した |
Assignment to constant variable. |
const に代入した → 別ページ |
このページの実行結果は Node 22.22.3 のものです。文言は処理系と版によって変わります。自分の環境で出た文言で検索してください。
探しかたの順序
- エラーの文に出ている名前を控える
- その名前の宣言を、同じ関数・同じブロックの中で探す
- 見つかったら、使っている行より下にないか見る
- 下にある → 宣言を上へ移す
- 外側の同名を使いたかった → 内側の名前を変える
- 見つからなければ、
varをletに変えた直後ではないか疑う
まとめ
- 宣言はある。まだ使える状態になっていない(一時的死角)
varはundefinedになるだけ。エラーになるほうが安全typeofでも避けられない- 関数宣言は巻き上がるが、
constに入れた関数は巻き上がらない classも巻き上がらない- 外側の同名を隠している場合がいちばん見つけにくい
'use strict' を書くと何が変わるかは、文字列の第1回でも同じコードを両方のモードで実行して比べています。
→ 文字列の基本