スポンサーリンク

JavaScriptの動かないコード(初級編)varで変数宣言する位置が,関数内の先頭以外だと起こる「変数の巻上げ(ホイスティング)」


以下のJavaScriptコードが意図した動作をしないのは,なぜですか。(制限時間1分)


やりたい事:

  • 関数の内容をアラートしたあとで,1と表示する。
<script>

function x(){

	// この関数の中身を文字列として表示する
	alert( x );

}


function y(){

	// この関数の中身を文字列として表示する
	alert( y );

	// 次に,1と表示する
	var y = 1;
	alert( y );

}


// 関数を実行する
x();
y();

</script>

発生する問題

alert(x)では,正常に関数オブジェクトの中身が文字列表示される。


しかし,yのほうがうまくいかない。

一回目のアラートではundefinedが表示され,二回目のアラートでは1が表示される。

関数内で,変数名を使いまわして途中でvarすると挙動がおかしくなる。

この問題の原因

じつはJavaScriptでは,どこにvarを書いても,関数内の先頭にvarを書いたのと同じ事になる。

そして,

  • 関数内の先頭ではundefinedがまず代入され,
  • 実際にvarによる変数宣言と代入が書いてある位置まで来たら,その時点で変数に値が代入される。


この現象のことを,「変数のホイスティング(巻き上げ)」という。

知らないと怖い「変数の巻き上げ」とは?|もっこりJavaScript|ANALOGIC(アナロジック)
http://analogic.jp/hoisting/

  • JavaScriptでは、関数内のどこにでもvar文を使用して変数を宣言することができます。そして、これらの変数は関数内のいかなる場所で宣言されたとしても、その関数の先頭で宣言されたのと同じように動作します。
  • JavaScriptでは、関数内で宣言されたローカル変数は、すべてその関数の先頭で宣言されたものとみなされる。 このような振る舞いは「変数の巻き上げ(hoisting)」と呼ばれます。


[JavaScript] 変数の巻き上げ(ホイスティング) | covellite
http://covellite.jp/blog/archives/81

  • どこで変数を宣言しても、実質はスコープの範囲の一番上で宣言されるのと同じ結果
  • しかし値が代入されるのはソースの記述位置になりますので、 値が最初に代入されるまでは変数が宣言されただけの状態、すなわちundefinedになる
  • これが変数の巻き上げ(ホイスティング)
  • ローカル変数は必ずスコープの一番上で作成しておいた方が良さそう


[javascript]実行されている関数はCallオブジェクトの中に含まれているのか : minoawのブログ
http://blog.livedoor.jp/minoaw/archiv...

  • JSは関数内のどこでvarしても一番上にあるのと同じ


ちなみに英語のhoistとは,クレーンでつり上げたり,旗や船の帆を上に巻き上げるということ。

※hoist the white flagで,白旗を掲げる,降参するというイディオム。


ここでは,

  • 関数の真ん中へんで宣言されているはずの変数が,クレーンなどで先頭まで吊り上げられてしまう,それが変数の巻上げ。

というイメージだ。

この問題の解決策と対処法

こういった現象を回避・予防するためには,

  • 「関数内の先頭でvarによる宣言をまとめて書く」

というコーディングスタイルを貫くとよい。


JavaScriptはJavaと異なり,for文やif文が作るブロックスコープというものは存在しない。

関数の先頭で,変数宣言は全てが決まってしまうのだ。


なので,先頭に明示的に変数宣言を集めておけば,それ以降の変数参照ミスも起きなくてすむ。

「単独varパターン」のススメ|もっこりJavaScript|ANALOGIC(アナロジック)
http://analogic.jp/single-var-pattern/

  • 変数を宣言する度にvar文を使うのではなく、複数の宣言をカンマで区切って全ての変数宣言をvarの1文で済ませます。 ここで重要なのは、関数内で使用する「全ての変数」を単一のvar文で宣言することです。


JavaScriptでも変数宣言はスコープの最初にまとめるべき - Ando.report(); // JavaScriptをもっと勉強したいブログ。
http://anderson.hatenablog.jp/entry/2...

  • Javaのようにforブロックやifブロックにスコープができる言語では、変数のスコープはできるだけ小さくなるブロックで変数を宣言します。 でもJavaScriptにはブロックスコープはありません。
  • したがって、JavaScriptという言語の中で一番小さいスコープ(=関数)の中で宣言するのがいい

関連する記事:

JavaScriptの動かないコード (初級編) 配列とオブジェクトの宣言エラー
http://language-and-engineering.hatenablog.jp/entry/20081010/1223605701


JavaScriptの動かないコード (初級編) parseIntで返り値が0になるエラー
http://language-and-engineering.hatenablog.jp/entry/20090314/p1


JavaScriptの動かないコード (初級編) カウンターのエラー
http://language-and-engineering.hatenablog.jp/entry/20080826/1219758515


JavaScriptの動かないコード (中級編) nullが0以上0以下と認識されてしまう
http://language-and-engineering.hatenablog.jp/entry/20090906/p1