自分で書いたJavaScriptをClosure Compilerにかけて圧縮していたのですが、その過程で妙なことに気が付きました。出力結果を見てみると、
function f(){
var a,c,d;
//中略
var c=foo(c),c=bar(c);
}
のように、同じ変数が何度も宣言されていました。一瞬バグを疑いましたが、どうやら正常な動作のようでした。
同じ変数の再宣言
明示的に変数を宣言可能な言語において、同じスコープ内で同名の変数を宣言すると、たいていはエラーになるものですが、JavaScriptに関してはそうではありません。
JavaScriptの関数内で行われた変数宣言は、それが関数のどこにあっても、宣言だけは関数の先頭にあるものと同等の動作をします(通称「巻き上げ」と呼ばれます)。
ECMA-262の10.5節の8番目では、コード実行時の変数宣言の処理について、以下のようになっています(わかりにくい単語などは適宜置き換えています)。
- コードの中に出てきた変数宣言を、順に処理する。
- Environment Record1にその変数があるかチェックする。
-
変数がなければ、Environment Recordに変数を作って、
undefined
をセットする。
このようになっていますが、すでに同名の変数が同じスコープにあった場合、3をスルーしてしまうので、何も起こりません。(初期値の代入は通常の代入として機能しますが)、変数宣言はエラーも出さず、何事もなかったかのようにコードは実行されます。
注意
意図的に複数回の宣言を書いても、1度だけ宣言した場合と動作は全く同じです。
//正常に動作はする(でもほぼ確実に無駄)
var c, c, c, c, c, c, c, c, c, c, c, c, c, c;
書けると言っても、実用のコードで書くべき場面は皆無と言っていいでしょう。
-
JavaScriptでは、グローバル変数はグローバルオブジェクト(ブラウザでは
window
)のプロパティとして存在しますが、それと同様に、関数の呼び出しごとに「Environment Record」というオブジェクトが作られ、引数やローカル変数はそのプロパティである、というように振舞います。ただし、このEnvironment Recordオブジェクト自体にアクセスする標準的な方法はありません。 ↩