【クイズでわかる】JavaScriptの巻き上げ(Hoisting)とTDZの仕組み

こんにちは!Reiです‎(˵ •̀ ᴗ – ˵) ✧

この記事を読みにきた皆さんは、
JavaScriptの挙動、ちゃんと理解できていますか?


たとえばこれ。どうなると思いますか?

console.log(food);
var food = "ラーメン";

パッと見だとエラーになりそうなんですが、実際はそうならないんですよね ( ; ˘-ω-)
どうしてだかわかりますか…?


チク(ง ˘ω˘ )วタク

チク(ง ˘ω˘ )วタク

チク(ง ˘ω˘ )วタク


このあたりで出てくるのが、
「巻き上げ(Hoisting)」と「TDZ(Temporal Dead Zone)」という仕組み。
(解説は後ほど!笑)

名前だけ聞くとちょっと難しそうですが、
やってること自体はそこまで複雑じゃないんです。


今回は、クイズ形式でコードの動きを確認しながら、JavaScriptの 巻き上げ と TDZ について解説していきます!
どこまで分かるか、ぜひ試してみてくださいᥫᩣ ̖́-


第1問 var の挙動

まずはここから!

console.log("好きな食べ物は?:", food);
var food = "ラーメン";
console.log("答えは?:", food);

このコードを実行するとどうなるでしょうか?

  1. エラーになる
  2. 1行目で undefined、3行目で「ラーメン」
  3. どちらも「ラーメン」

(;´・ω・)ウーン・・・


正解は… (ここをクリック!)

2 でした!
1行目で undefined、そのあと3行目に「ラーメン」が表示されます。


解説 !!

ここで出てくるのが「巻き上げ(Hoisting)」です。
ざっくり言うと、変数の宣言をundefinedの状態で用意して先に読み込まれるイメージ


実際の動きはこんな感じになっています↓

var food; // ← 宣言(箱)が先に用意される(undefinedの状態で巻き上げ)
console.log(food);
food = "ラーメン";
console.log(food);

「宣言(箱)だけ先にある状態」になるので、エラーにはならず undefined が出るのです。


ここでポイントなのが、宣言と代入は別ということ。

  • var food; → 先に読み込まれる
  • food = "ラーメン"; → あとで実行される

なので、1行目の段階では「変数はあるけど中身がまだない状態」だったので、 undefined と出力されたのでした!


第2問 let と TDZ

次は let について!

console.log("猫の種類は?:", cat);
let cat = "ハチワレ";

さて、どのように動くでしょうか?

  1. undefinedになる
  2. エラーになる

正解は… (ここをクリック!)

2の「エラーになる」でした!
(ここが一番ハマりやすいところかも…!)


解説 !!

letconst も巻き上げ自体は起きるのですが、
変数の宣言はしているものの、まだ使える状態じゃない んです。

これが「TDZ(Temporal Dead Zone)」というもの。


実際の動きをイメージするとこんな感じです↓

// イメージ的にはこういう状態

let cat; // ← 宣言(箱)だけ先に用意(でもまだ使えない)
console.log(cat); // ← 使えない状態なのでエラーになる ⚠️
cat = "ハチワレ";

第1問の var と違うポイントはここ!

var → 宣言された時点で undefined が入る
let / const → 宣言されても中身がまだない状態のまま

なので、「変数は存在しているけど、まだ触っちゃダメな状態」になっています。


この 触れない期間 が TDZ なのです。


letconst は宣言にたどり着くまでの間は、箱はあるもののまだ使えない状態。
だからアクセスするとエラーになるのです。

同じように見えて、var と違うところだから注意!


第3問 関数の呼び出し

最後は関数です!

sayHello();
function sayHello() {
console.log("こんにちは!");
}

sayHi();
const sayHi = () => {
console.log("やっほー!");
};

さて、どのように動くでしょうか?

  1. どちらも問題なく実行できる
  2. sayHello は動くが、sayHi はエラーになる
  3. どちらもエラーになる

正解は… (ここをクリック!)

2の「sayHello は動くが、sayHi はエラーになる」でした!


解説 !!

ここもさっきと同じで、「巻き上げ」が関係してます。

ただし今回は、関数の書き方の違いがポイント!


まずはこちら↓

function sayHello() {
console.log("こんにちは!");
}

これは 関数宣言 と呼ばれる書き方で、
関数の中身ごとまるっと巻き上げられます。

つまり、実際の動きをイメージするとこんな感じ↓

function sayHello() { // ← 関数が先に用意される(巻き上げ)
console.log("こんにちは!");
}
sayHello(); // ← 関数が定義されているので動く

sayHello は呼び出しより前に用意されているので、ちゃんと動くんです。


一方でこちら↓

const sayHi = () => {
console.log("やっほー!");
};

これは 変数に関数を入れている形 になります。
つまり、第2問の let / const と同じ扱いになるのです!

実際の動きをイメージするとこんな感じ↓

const sayHi; // ← 宣言(箱)だけ先に用意(でもまだ使えない)
sayHi(); // ← 使えない状態なのでエラーになる ⚠️
sayHi = () => {
console.log("やっほー!");
};

宣言にたどり着くまでは使えない状態なので、ここでエラーになるのです。


ここまでの流れをまとめると…

・ function で書いた関数 → 先に使える状態になる
・ const に入れた関数 → 宣言されるまで使えない

同じ「関数」でも、書き方で挙動が変わるので注意です!


ここまでのポイントおさらい !!

JavaScriptって、見たままの順番でそのまま動いてるわけじゃないんですよね ( ˘•ω•˘ ;)ムズカシイ…

今回みたいに「宣言の扱い方」が変わるだけで、
結果がガラッと変わるのがちょっとややこしいところ…!


ここまでの内容をざっくり整理すると、こんな感じ↓

var

  • 宣言が巻き上げられる
  • 中身は undefined

let / const

  • 宣言が巻き上げられる
  • 宣言されるまでは使えない(TDZ)

関数

  • function → 中身ごと巻き上げられる
  • const → 変数扱いなので宣言されるまでは使えない

同じように見える部分ですが、
実際の動きはそれぞれしっかり違いがあります 👀


なので普段書くときは…

  • var は使わない
  • const / let を使う
  • 関数も呼び出しより前に書いておく

このあたりをちょっぴり意識して、
今回の挙動を思い出してもらえたら嬉しいです ᥫᩣ ̖́-

コメント

タイトルとURLをコピーしました