今更ながらJavaScriptのホイスティングを説明してみる
Vue.jsのソースコードを読んでいた際、関数宣言エクスポートされているisReactive
内で、isReactive
以降で関数宣言エクスポートされているisReadonly
が実行されていることに違和感を持ちました(例1参照)。
//例1
export function isReactive(value: unknown): boolean {
if (isReadonly(value)) { // isReadonlyって宣言の前に実行されてる??
return isReactive((value as Target)[ReactiveFlags.RAW])
}
return !!(value && (value as Target)[ReactiveFlags.IS_REACTIVE])
}
export function isReadonly(value: unknown): boolean {
return !!(value && (value as Target)[ReactiveFlags.IS_READONLY])
}
恥ずかしながら、関数宣言は関数の実行前に記載されなければならないと思っていました。調べてみるとホイスティング(巻き上げ)という現象が関係しているようで、今回はホイスティングについて整理してみます!
※👇読んでいたVue.jsのソースコード
https://github.com/vuejs/core
ホイスティング(巻き上げ)とは
コンテキスト内で宣言した変数や関数の定義をコード実行前にメモリに配置すること。
イメージ的には、変数や関数の宣言はスコープの最初に行われ、その他の処理は基本的に記載順に実行されると理解しています。
具体例
ホイスティングによって以下のような挙動が発生します。
変数の話
var
を使用した場合
var
を利用して変数宣言をする場合、まずはじめに変数test
のメモリを確保し、undefined
で初期化されます。その為、変数宣言より前に変数を呼び出すと、undefined
が出力されます(例2参照)。
//例2
console.log(test) // undefinedが出力される
var test = 0
console.log(test) //0が出力される
let
やconst
を使用した場合
しかし、let
やconst
を利用して変数宣言をした場合、変数test
のメモリは確保されるものの、undefined
での初期化が行われません。その為、変数宣言より前に変数を呼び出すとエラーとなります(例3参照)。
//例3
console.log(test) // エラー「Cannot access 'test' before initialization」
let test = 0
console.log(test) //0が出力される
関数の話
そもそも関数の定義方法
関数の定義方法は、大きく分けると①関数宣言、②関数式の2パターンが存在します(例4参照)。
//例4
//関数宣言
function example(){}
//関数式
var example = function(){} // let example = function(){} こんなんも
関数宣言の場合
関数宣言を使用する場合、関数example
は、その実行前にメモリ上に配置されます。その為、下記のようにexample
を、実行より下で関数宣言しても、example
は実行されます(例5)。
//例5
example(); //テストが出力される
function example(){
console.log('テスト');
}
関数式の場合
関数式の場合は、例2〜例3で記載した変数と同じような挙動が発生します。
var
を使用した関数式の場合、example
を実行しようとしても、undefined
で初期化されているため、関数ではないと怒られます(例2、例6.1参照)。let
を使用した関数式では、example2
が初期化されていない為、定義される前に呼び出すとエラーとなります(例3、例6.2参照)。
尚、無名関数の省略記法であるアロー関数で関数式を利用した場合も、これらのようなエラーが発生します。
// 例6.1 varの関数式
example() //undefinedなので、「example is not a function」と怒られる
var example = function(){
console.log('テスト');
}
//例6.2 letの関数式
example2() //エラー「Cannot access 'example2' before initialization」
let example2 = function(){
console.log('テスト2');
}
最後に
今回の整理で、Vue.jsのソースコードで抱いた疑問は解消されました!雰囲気でJavaScript(とTypeScriopt)を書いてしまっているので、今年は疑問に思ったことは放置せず、理解度をあげて、自分の言葉で語れるようになりたいと思います。
※もし、間違い等があればご指摘頂けますと、非常に嬉しいです。
参考
Hoisting (巻き上げ、ホイスティング) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN
Discussion
素晴らしい👍
勉強になりました!
typo があります。
本文中の
undefiend
→undefined
ではないでしょうか?typoしておりました、、、
修正しました!ご指摘ありがとうございます!!