はじめまして!Qiita初投稿になります。
最近JavaScriptの勉強を始めました。
折角なので学習したことをアウトプットしていきたいと思います。
基本的な内容が多いですが、私と同じ初学者方の参考になれば幸いです。
また至らない点がありましたら、ご指摘いただけると嬉しいです。
今回はJavaScriptを勉強中に、ホイスティングという耳慣れない用語が登場したので調べて見ました。
#ホイスティングとは
変数や関数の定義をコードが実行される前にメモリに配置する挙動のことを指します。
その挙動から「宣言の巻き上げ」とも言われます。
言葉ではイメージしづらいので早速コードを見ていきましょう。
###関数のホイスティング
f(); //f関数を実行
function f(){
console.log("Hoisting Test"); // Hoisting Test
}
こちらのコードですが、一見すると実行文が関数の定義より前にあり、エラーになりそうですが問題なく動作しています。
これはホイスティングによってコードが実行される前に関数の定義がメモリに配置されているため、実行することができています。
これがホイスティングによる動きになります。
続いて変数のホイスティングを見てみましょう。
###varを使った場合
console.log(x); // undefined
var x = 0; // 変数xの宣言
今度は変数xの宣言よりも前にconsole.logでxの値を取得しています。
こちらも一見エラーになりそうですが、undefinedが出力されます。
変数も関数同様、ホイスティングによってコード実行前に変数xのメモリを確保しているので、console.logで問題なく参照することができています。
注意点としては、行っているのはメモリの確保のみで、値は代入されていないので中身はundefined(未定義)となります。
undefinedは値が未定義の場合に自動で代入されるキーワードになります。
イメージとしては以下になります。
// 事前に変数xのメモリ確保とundefinedの代入を行ってからコードを実行!
console.log(x); // 空の中身を出力。
var x = 0; // 0を代入。
var変数ではこういった処理がコード実行前に行われるため、エラーが発生することなく変数の値を出力することができます。
###let、const場合
console.log(x); // ReferenceError
let x = 0;
console.log(x); // ReferenceError
const x = 0;
let、const変数の場合は、
[Uncaught ReferenceError: Cannot access 'x' before initialization]
というエラーメッセージが表示されます。
これは**変数xに値が値が入っていませんよ(初期化されていない)**というエラーです。
どういうことかと言うと、let、constではvarのように自動でundefinedが代入されません。
よって、メモリ確保はされているので参照することはできますが、変数の値を取得しようとしてもエラーが発生することになります。
個人的にエラーがでてくれた方が安心感があります。
ということで、varとlet、constではホイスティングの挙動が異なるということになります。
###予期せぬエラー
さて、関数や変数のホイスティングの動きを見てきましたが、この挙動を理解していないと意図していない値の取得、エラーの原因となります。
例えば、以下のコードを見てみましょう。
let x = 10; //グローバルスコープの変数x。
{
console.log(x); // グローバルスコープの変数xを取得したい。でもエラー
let x = 0; //後からブロックスコープで変数xを宣言。
}
グローバルスコープで宣言した変数xの値をブロックスコープ内で取得したい時、誤って後から同名の変数xをブロックスコープで宣言してしまうと、ReferenceErrorが発生してしまいます。
ホイスティングはスコープごとに発生するのですが、今回はブロックスコープ内で宣言した変数xがホイスティングにより、中身が無い状態でブロックスコープの最初に定義されるため、参照されるのはブロックスコープの変数xとなります。
そしてletの中身は空なので、値を出力しようとしてもReferenceErrorになるわけです。
イメージとしては以下になります。
let x = 10; //グローバルスコープの変数x。
{
// x 中身が無い変数xがここにある。
console.log(x); // 上の空のxを参照する。
let x = 0; //ここで初めて値が入る
}
この場合、グローバル変数xの値を取得するには、ブロックスコープの変数名を変更してあげれば良いのですが、慣れないうちは予期せずこういったエラーに遭遇するかもしれません。
ホイスティングの動きを理解していれば冷静に対処できそうです。
##まとめ
ホイスティングについて記述しました。ホイスティングによって変数や関数の定義が事前にメモリに配置されるということになります。慣れないうちはホイスティングによる挙動を意識してコードを記述していきたいと思います。
至らない点がありましたら、ご指摘いただけると嬉しいです。
##参考
https://developer.mozilla.org/ja/docs/Glossary/Hoisting
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/var