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