【JavaScript】 can’t access lexical declaration before initializationってなに?
更新日:2020/07/09
JavaScriptコード実行時、次のようなエラーで処理が止まることがあります。
Firefox:
『ReferenceError: can't access lexical declaration `・・・' before initialization』
Google Chrome:
『Uncaught ReferenceError: Cannot access '・・・' before initialization』
これって何でしょうか?
初期化前にアクセスできません
エラーを日本語に翻訳してみます。
Firefox:
『初期化前に字句宣言 `・・・'にアクセスできません』
Google Chrome:
『初期化前に '・・・'にアクセスできません』
つまり『初期化していないのに変数にアクセスしている』ということです。
初期化前にアクセスされるケースを再現
初期化前にアクセスされる状況を再現してみます。
変数を宣言しない場合
変数を宣言しないで、console.log()で表示してみます。
console.log( a ); // ReferenceError: a is not defined
例外がスローされて処理が停止しました。
ReferenceError: a is not definedは、変数aがどこにも定義されていないという意味です。
変数を宣言する場合
次は、変数を宣言してからconsole.log()で表示してみます。
なお初期値をセットしません。
let letValue;
var varValue;
const constValue;
console.log( letValue ); // undefined
console.log( varValue ); // undefined
console.log( constValue ); // SyntaxError: missing = in const declaration
letやvarは、undefinedがセットされています。
constは例外がスローされて処理が停止しました。
constは後から変更できなので、宣言時に値をセットすることを強制されます。
undefinedは値が定義されていないという意味ですが、実際にはundefinedという値が変数にセットされています。
変数にアクセスした後宣言する場合/原因の答え
次は変数を宣言しますが、その前にconsole.log()で表示します。
console.log( letValue ); // ReferenceError: can't access lexical declaration `a' before initialization
console.log( varValue ); // undefined
console.log( constValue ); // ReferenceError: can't access lexical declaration `c' before initialization
let letValue = 1;
var varValue = 2;
const constValue = 3;
letとconstで、問題となっているエラーが再現されました。
つまり、letとconstで宣言をする前に変数にアクセスしたことが、エラーが発生した原因です。
変数の巻き上げが原因
JavaScriptはコードの評価時に変数の巻き上げという処理をおこない、関数内の変数をリストアップします。
このとき、varは初期値としてundefinedがセットされます。
しかしletとconstは、何もセットされません。
名前はわかっているけれど、中身がない状態です。
中身がセットされるのは、実際にコード上でイコールを使用して代入されたときです。
それ以前に変数にアクセスすると、文字通り『初期化前に '・・・'にアクセスできません』というエラーが発生するのです。
なお巻き上げという言葉は、下のコードのように、評価時に関数の先頭に移動(巻き上げ)される様子を表しています。
変数の巻き上げ
function func(){
/* 評価時に巻き上げられたコード */
let letValue; // 巻き上げでリストアップされた変数。値がセットされていない
var varValue ; // 巻き上げでリストアップされた変数。値はundefined
const constValue; // 巻き上げでリストアップされた変数。値がセットされていない
/* 以下実際のコード */
console.log( letValue );
console.log( varValue );
console.log( constValue );
let letValue = 1; // 値を初期化
var varValue = 2; // 値をundefinedから2に変更
const constValue = 3; // 値を初期化
エラーが出るパターン
どんな時に、エラーになるのかパターンを挙げておきます。
変数の定義前にアクセスする
変数の定義前に変数にアクセスすると、エラーとなります。
console.log( a ); // ReferenceError: can't access lexical declaration `a' before initialization
let a = 1;
関数式の定義前に関数を実行する
関数式の定義前に関数を実行すると、エラーとなります。
a(); // ReferenceError: can't access lexical declaration `a' before initialization
const a = function(){ };
関数式の代入先は変数なので、本質的には変数の定義前にアクセスすると同じです。
関数内で関数を呼ぶ
関数内で関数を実行するとき、実行する関数が初期化されていないとエラーになります。
const a = function () {
b();
};
a(); // ReferenceError: can't access lexical declaration `b' before initialization
const b = function () {
console.log( "test" );
}
a(); // この位置ならエラーにならない。
上のコードは、関数bの定義後に関数aを実行すればエラーになりません。
コードが長くなり見通しが悪くなると、発生しやすいエラーです。
更新日:2020/07/09
関連記事
スポンサーリンク
記事の内容について
こんにちはけーちゃんです。
説明するのって難しいですね。
「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。
裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。
掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。
ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php
このサイトは、リンクフリーです。大歓迎です。