こんにちは!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行目で undefined、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 = "ハチワレ";
さて、どのように動くでしょうか?
- undefinedになる
- エラーになる
正解は… (ここをクリック!)
2の「エラーになる」でした!
(ここが一番ハマりやすいところかも…!)
解説 !!
let や const も巻き上げ自体は起きるのですが、
変数の宣言はしているものの、まだ使える状態じゃない んです。
これが「TDZ(Temporal Dead Zone)」というもの。
実際の動きをイメージするとこんな感じです↓
// イメージ的にはこういう状態
let cat; // ← 宣言(箱)だけ先に用意(でもまだ使えない)
console.log(cat); // ← 使えない状態なのでエラーになる ⚠️
cat = "ハチワレ";
第1問の var と違うポイントはここ!
var → 宣言された時点で undefined が入るlet / const → 宣言されても中身がまだない状態のまま
なので、「変数は存在しているけど、まだ触っちゃダメな状態」になっています。
この 触れない期間 が TDZ なのです。
let や const は宣言にたどり着くまでの間は、箱はあるもののまだ使えない状態。
だからアクセスするとエラーになるのです。
同じように見えて、var と違うところだから注意!
第3問 関数の呼び出し
最後は関数です!
sayHello();
function sayHello() {
console.log("こんにちは!");
}
sayHi();
const sayHi = () => {
console.log("やっほー!");
};
さて、どのように動くでしょうか?
- どちらも問題なく実行できる
- sayHello は動くが、sayHi はエラーになる
- どちらもエラーになる
正解は… (ここをクリック!)
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 を使う
- 関数も呼び出しより前に書いておく
このあたりをちょっぴり意識して、
今回の挙動を思い出してもらえたら嬉しいです ᥫᩣ ̖́-


コメント