内容
変数の巻き上げについて書く。
変数の巻き上げ
グローバルスコープ内で変数を宣言すると、宣言処理はグローバルスコープの先頭に巻き上げられる。
例えば、以下のようなコードがあったとする。
console.log("1行目:" + x); // 変数の宣言前に変数xを使用
var x = "initialize"; // 変数xを宣言
console.log("3行目:" + x);
普通に考えると、1行目は変数の宣言前なのでReferenceErrorになる気がする。
が、実行してみるとこうなる。
1行目:undefined
これは、変数の巻き上げによって、変数xの宣言処理がコードの先頭に巻き上げられたため。
イメージとしては、こんな感じになっている。
var x; // 変数の宣言処理だけ、先頭に移動
console.log("1行目:" + x);
x = "initialize"; // 変数の初期化処理はそのままの位置
console.log("3行目:" + x);
関数内での変数の巻き上げ
関数内で変数が宣言されている場合も変数の巻き上げは発生する。
関数内で変数が宣言されている場合は、その関数の先頭に巻き上げられる。
例えば、こんなコードがあったとする。
これは関数内での巻き上げが発生しないやつ。
var x = "A";
function testPrint(){
console.log(x);
}
testPrint();
こいつの動作結果は、こんな感じ。イメージ通りの動きである。
A
一方、これは関数内で変数を宣言して巻き上げが発生するやつ。
var x = "A";
function testPrint(){
console.log(x);
var x = "B";
}
testPrint(); // undefined
この実行結果は、こうなる。
undefined
何でそうなるかっていうと、巻き上げによってこんな感じの動作になっているから。
関数の先頭でxが再宣言されることで、console.log(x)がundefined(未定義)になるんすね。
var x;
x = "A";
function testPrint(){
var x;
console.log(x);
x = "B";
}
testPrint(); // undefined
関数の宣言も巻き上がるぞ
変数だけじゃなくて関数も巻き上がる。
関数の巻き上げは、割と自然に受け入れられる。
例えば、これは巻き上げの例。
testPrint("Yamada");
function testPrint(item) {
console.log(item);
}
こいつの実行結果は、こうなる
Yamada
割と普通の動きに見える。
でも、よく考えるとtestPrint関数が定義される前にtestPrint関数を呼び出しているのに、普通に呼び出せてるのはなんでだろう。
理由は、巻き上げによってこんな感じの動作になってるから。
function testPrint(item) { // 巻き上げで関数の定義処理が先頭に移動した
console.log(item);
}
testPrint("Yamada");
つまり、巻き上げがあることによって、関数はどのタイミングでも呼び出せるようになってるってこと。
でも、関数式は巻き上がらないぞ
ややこしいけど関数式を使うとまきあがらない
関数式は宣言じゃないからね。関数式を代入された変数の方は、もちろん巻き上がる。
これは、変数が巻き上がることを確認するコード。
console.log(myFunction); // => undefined
var myFunction = function() {
console.log('Hello');
};
こっちは、関数式でかかれた関数は巻き上がらないことを確認するコード。
myFunction(); // => TypeError: myFunction は関数ではない
var myFunction= function() {
console.log('Hello');
};
変数であっても定義を書かなければ巻き上がらない
変数であっても、定義処理を書かなかったら巻き上がらない。
例えば、これは巻き上がらない
console.log(x); // => ReferenceError 例外が発生
x= “initialize”; // varを省略して初期化
letやconstは、巻き上がらない(ようにふるまう)ので安心
letやconsotを使うと巻き上がりを無視して実装できる。
正確にはletもconsotも巻き上がるが、変数の初期化処理が行われるまでJavaScriptが一時的にプログラムから見えなくしてくれる。ので、巻き上がってないかのように使える。
こんなコードがあるとする。
console.log(x); // => ReferenceErrorが発生。巻き上がってないように見える。
let x = “initialize”; // 変数xの宣言処理
実際は、こんな感じで巻き上がるけ。
けど、不思議な大地の力で他の処理から変数が見えなくなってなかったことにされる。
let x; // 宣言処理は先頭に巻き上がる
console.log(x); // => ReferenceErrorが発生。不思議な力で変数xが隠される。
x = “initialize”; // 初期化
参考
https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Grammar_and_types
https://developer.mozilla.org/ja/docs/Glossary/Hoisting