IE8- は名前付き関数式を関数宣言としても扱う

ECMAScript の 名前付き関数式

ECMAScript には「名前付き関数式」があり、変数に関数式を代入するときに名前をつけることが出来ます。

var hoge = function foo () {
  alert(foo);          // function foo() { ... }
  alert(foo === hoge); // true
};

ここでは "foo" という名前の関数を作りました。関数fooの内部では foo で関数オブジェクトを参照できますが、関数fooの外部では参照できません。

// 関数宣言
function piyo () {
  alert(piyo);
}

// 名前付き関数式
var hoge = function foo () {
  alert(foo);
};

piyo(); // 実行できる
hoge(); // 実行できる
foo();  // ReferenceError: foo is not defined||<

IE8- の名前付き関数式

IE8- では名前付き関数式を関数宣言としても扱うバグがあります。(IE9 で修正済み)

foo();               // 実行できる
alert(foo === hoge); // false

var hoge = function foo () {
  alert(foo === hoge); // false
};

これは以下のコードとほぼ同じ動作です。

/**
 * 関数宣言
 */
function foo () {
  alert(foo === hoge); // false
}

/**
 * 関数式 (本来は名前をつけますが、IE8- と挙動を合わせるためにあえて匿名関数にしています)
 */
var hoge = function () {
  alert(foo === hoge); // false
};

foo();               // 実行できる
alert(foo === hoge); // false

hoge.toString() の結果が異なる点を除いて、IE8- と Google Chrome 11 で同じ結果を得られます。

  • 関数hogeの内部で foo を参照できるのは関数宣言fooを参照しているためです。
  • 関数宣言と関数式の代入は別々に行われるため、同じ関数オブジェクトが2つできてしまいます。
  • 関数宣言と関数式は別物であるため、hoge === foo とはなりません。

IE8- で本来の名前付き関数式の振る舞いにする

名前付き関数式の代わりに関数宣言を関数スコープに閉じこめてあげれば、本来の名前付き関数式とほぼ同じように振る舞います。

var hoge = (function () {
  function foo () {
    alert(foo);
  }

  return foo;
})();