JavaScriptのホイスティング(Hoisting)について
JavaScriptのホイスティングについてまとめました。
JavaScriptのホイスティング(Hoisting)=宣言の巻き上げ
ホイスティング(Hoisting)とは、コンテキスト内で宣言した変数や関数の定義をコード実行前にメモリに配置することです。
ホイスティングのことを「宣言の巻き上げ」といったりもします。
変数が宣言される前に、その変数をアクセス可能にしたり、利用可能にしたりすることです。
コード実行前に、変数宣言にコードスキャンします。
そして、それぞれの変数のために、変数環境のオブジェクトの中で新しいプロパティを作ります。
a();
function a() {
console.log('a');
}
上の例のように関数の前で呼び出しても実行できるのは、コード実行の前に関数がすでにメモリ上に存在するためであり、つまりホイスティングがあるためです。
ホイスティングはコンテキストが生成されるたびに起こります。
ホイスティングと初期値とスコープの関係
ホイスティングあり? | 初期値は? | スコープは? | |
---|---|---|---|
関数宣言(function declaration) | あり | 関数(Actual function) | ブロックスコープ(strict modeの場合のみ) |
var | あり | undefined(JavaScriptの有名なバグともいわれる) | 関数スコープ |
let / const | なし(厳密にいうとありだけど実際エラーにはなる) | uninitialized(Temporal Dead Zone, TDZ、一時的デッドゾーン) | ブロックスコープ |
関数式とアロー関数(function expressions and arrows) | varかlet/constと同じ動き | varかlet/constと同じ動き | varかlet/constと同じ動き |
関数宣言(function declaration)はfunction func1(n) {}という形。
関数式はconst func2 = function (n) {}という形。
varをJavaScirptエンジンが見つけたら、スペースを確保してundefinedをメモリにセットします。
let / constでのホイスティングは、行われているものの、宣言の前に使おうとしても使えません。それはTDZという領域ができていて、初期値を設定してからでないと使えません。
Temporal Dead Zone, TDZ、一時的デッドゾーンについて
次のコードをみてみましょう。
const name = 'yamada';
if(name === 'yamada') {
consol.log(`yamada's age is ${age}`);
const job = 'engineer';
console.log(job);
const age = 40;
console.log(y);
}
上のコードの場合、ageはifのブロックスコープ内にありますが、その宣言される前の部分はすべてTemporal Dead Zone(TDZ、一時的デッドゾーン)となります。
初期化が必要ですといったエラーが返ってきます。
ReferenceError: Cannot access 'age’ before initialization
これはホイスティングはしているので変数ageがあることがわかってはいるものの、初期値を設定する前には使えませんよということです。
本当に宣言もしていないたとえば変数yに関しては次のように定義されていませんというエラーが返ってきます。
ReferenceError: y is not defined
このTDZはより簡単にエラーを発見するためにES6から導入されました。
このエラーのおかげでとても発見しにくいバグを防ぐことができます。
ホイスティングの挙動の具体例
//変数とホイスティング
console.log(yamada); //undefinedが返ってくる
console.log(job); //TDZにあるため、初期化されていないというエラーが返ってくる。
console.log(year); //TDZにあるため、初期化されていないというエラーが返ってくる。
var yamada = 'yamada';
let job = 'teacher';
const year = 2000;
//関数とホイスティング
console.log(addFcDecl(2,3));//正常に答えが返ってくる。
console.log(addFcExpr(2,3));//TDZにあるため、初期化されていないというエラーが返ってくる。
console.log(addArrow(2,3));//TDZにあるため、初期化されていないというエラーが返ってくる。
function addFcDecl(a, b) {
return a + b;
}
const addFcExpr = function(a, b) {
return a + b;
}
const addArrow = (a, b) => a + b;
var addArrow = (a, b) => a + b; // is not a functionというエラーが返される。undefinedだから。試しにconsole.log(addArrow);とするとundefinedで返される。