「JavaScriptを学んでるんだけど、急に undefined とか出てきて意味わかんない!
なんか“ホイスティング”とか言われてもピンとこないし…正直めんどくさい!!」
そんな衝撃を受けているそこのあなた。大丈夫です、みんな最初はそう思います。
というわけで、今回は JavaScript独特の「ホイスティング」 について、ムリなくわかるようにまとめてみました。
そもそもホイスティングって何なの?(震え声)
ホイスティング(hoisting) とは、「変数や関数の宣言が、実際に書いた位置より上にあるかのように扱われる仕組み」のことです。
要するに、JavaScriptエンジンが「このコードを動かす前に、変数や関数をまとめて宣言しておこう」と裏で準備しちゃうわけですね。
そのおかげ(?)で、宣言前に変数を使っているのにエラーにはならなかったり、
逆に「エラーになるはずがないのになぜかエラーが出た…」みたいな状況が起こるわけです。
1. var のホイスティング
コード例
console.log("変数 myVar の値:", myVar);
// ここでは "undefined" と表示(なぜエラーにならない!?)
var myVar = "Hoisted!";
console.log("変数 myVar の値:", myVar);
// ここでは "Hoisted!" と表示
解説
見てのとおり、宣言より前の行で myVar を出力しているのに、エラーが起きないのは不思議ですよね。
これは JavaScriptが「varで宣言された変数」を先に確保(宣言だけ)しているから なんです。
しかし、変数が「存在」するだけで値の代入は後になるので、最初の出力は undefined、その後に "Hoisted!" が入るという流れになります。
要するに「変数名だけ先に登録しておいて、中身は後で入れる」というイメージです。
(実行したときの挙動と、コードの見た目が一致しないので、ちょっとややこしいんですよね。)
2. 関数宣言 vs 関数式
「じゃあ関数も同じなの?」と思ったら、これがまたホイスティングをさらに複雑に見せるポイントです。
(1) 関数宣言
コード例
// --- function宣言の場合 ---
hoistedFunction();
// => "function宣言のhoistedFunctionが呼び出されたよ!" と表示される
function hoistedFunction() {
console.log("function宣言のhoistedFunctionが呼び出されたよ!");
}
解説
function hoistedFunction() { ... }
という書き方を関数宣言と呼びます。
「変数の宣言」と違って、関数の本体そのものもまとめて上に持ち上げられるため、宣言より前の場所でも関数を呼び出すことができます。
(2) 関数式(function式)
コード例
// --- function式の場合 ---
hoistedExpression();
// => 実行時にエラー!!! ("まだ中身が入ってない!")
var hoistedExpression = function() {
console.log("function式のhoistedExpressionが呼び出されたよ!");
};
解説
var hoistedExpression = function() { ... };
の書き方をfunction式といいます。
これは「変数に関数を代入している」だけなので、変数自体が先に宣言されるだけで、実際の「関数の中身」が代入されるのは後からになります。
結果として、宣言より前の行で呼び出そうとすると「まだ関数が入ってない!」とエラーになるわけですね。
3. let / const の場合は?
最近のJavaScriptでは、var よりも let や const を使うのが一般的です。
これらを使うとホイスティングはどうなるんでしょうか?
コード例
console.log("myLetVar の値:", myLetVar);
// => ここはエラー(ReferenceError)!
let myLetVar = "これは myLetVar だよ";
console.log("myLetVar の値:", myLetVar);
// => "これは myLetVar だよ"
解説
var のときは undefined になっていたのに、let や const だとエラー。
これは 「宣言される前に使ったらダメ!」 というルールがあるからです。
こうすることで、「うっかりミスで宣言前に変数を使っちゃった!」みたいな状況を減らせるわけですね。
いわば、安全装置みたいなものと考えるといいかもしれません。
まとめ
ホイスティングとは
- 実行前に変数や関数宣言をまとめて上に持ち上げてしまう(hoisting)JavaScriptの仕組みのこと
- varの場合
- 変数の「存在」だけが先に作られるため、宣言前に呼び出すと undefinedtとなる
- function宣言の場合
- 「関数本体」も含めて先に読み込まれるので、宣言前から呼び出せる
- function式 (
var x = function() {...}
)の場合- 変数だけ先に用意される。関数本体は後から代入されるので、宣言前の呼び出しはエラーとなる
- let や constの場合
- 宣言される前に使うとエラーになる
- 「宣言前に使っちゃダメ!」が明確になっているので、安全性が高い
要するに、「宣言はなるべくコードの上部にまとめる」 ことやですね。
「let と const で変数を管理する」 ことで、ホイスティングによる混乱を減らすことができます。
おわりに
ホイスティングって最初は「えっなんで?」となりがちですが、JavaScriptをちゃんと理解するうえで避けては通れないポイントです。
「思った通り動かないぞ?」というときは、「あ、ホイスティングのせいかな」と疑ってみましょう。
そのうえで、宣言位置を見直してみたり、let / const を使うようにしてみたりすると、すっきり解決するかもしれません。
そんなわけで、ホイスティングをざっくり解説してみました。
実際にコードを書いてみると「本当に undefined なの?」とか「まじでエラーになるの?」と体感できるはず。
ぜひ試してみて、「おお、こういうことだったのか!」と納得してもらえたら嬉しいです!