この記事について
Hoistingができるタイプの変数とできないタイプの変数があるので整理する。
■Hoistingとは
宣言をする前でもその変数を使えることができる仕組み
変数のタイプ
① 関数宣言(Function Declaration) (関数に名前があるタイプ)
例)function calc(a, b) {return a + b }
② varでの変数宣言
③ const や letでの変数宣言
④ 無名関数(Function expression or arrow function) (関数に名前がないタイプ)
例)const calc = function(a, b) { return a + b }
Hoistingができるのは...
① 関数宣言
関数宣言の書き方では、宣言する前に関数を呼んでいるのに、ちゃんと戻り値5で返ってくるんです。(Hoistingが機能する)
console.log(calc(2, 3));
function calc(a, b) {
return a + b;
}
コンソール上でのログ↓
Hostingによって宣言前でもエラーにならず動くのですね。
② varで宣言した変数
変数を定義する前にcalc変数を使ってみます。
console.log(calc);
var calc = 2 + 3;
コンソール上の結果はこちら
エラーにはならないものの、undefinedで結果が表示されます。
③letやconstで宣言した変数
②と同じコードをletで書くと
console.log(calc);
let calc = 2 + 3;
コンソール上の結果はこちら
エラーになりますね。
これはletやconstには、Temporal Dead Zone(TDZ)が適用されるから。
TDZは、そのスコープ内で、使いたい変数が定義される(この場合let calc = 2+3
)以前に書かれたコードに対してはその変数は使えないよという意味。
なので上記の例では、「initializationの前には'calc'は使えません」ってエラーが出ています。
④無名関数
①と同じコードを無名関数で書くと
console.log(calc(2, 3));
let calc = function (a, b) {
return a + b;
}
console.logで結果を確認すると、エラーになっています。
③で見たエラーと同じですね。letで書いているのでTDZが適用されています。
ちなみに、無名関数をvarで書くとこのようなエラーになります。
②で見た例では、var宣言したものはエラーにならずundefinedと表示されました。
これは、varで定義するとundefinedとなりますが、undefinedという名前の関数のようになってしまい、「calc is not a function」とエラーが出てしまっています。
Hoistingが適用されるとこんなことにも...
if(!num) delete();
var num = 10;
function delete() {
console.log(`deleted!`);
}
↑はvarの定義をif文より上に書けば問題ないのですが、もし間違えて下に書いてしまった時の例です。
if文で0の初期値はfalseなのを利用して「!でfalse->trueにして0だったらdelete()を実行、0以外の数字だとfalseになりdelete()は実行しない」という意図で上記コードを書いたとします。
「numは10が入るのでdelete関数は動かない」なんて思ってしまったらバグになってしまいます。varで定義されているためHoistingが適用されて最初のif文でのnumはundefinedが入るんです。
undefinedの初期値はfalseなので、!で反転してtrueになり、delete関数が動きコンソール上に「deleted!」と表示されてしまいます。
一方constやletで書いていたとするとTDZが適用されてエラーになってくれます。
感想
letやconstを使っているならエラーが出るので大丈夫ですが、varを使用して意図せずHoistingが適用されりすると見つかりにくいバグができたりするのかなーと思いました。
Hoistingの仕組みを知っているとバグ調査の時も、「varで書かれてるからもしやHoistingが適用されているのかな?」と疑ってみることもできるので、大事な知識だと感じました。