Hatena::ブログ(Diary)

latest log このページをアンテナに追加 RSSフィード

2009-07-01

関数の初期化とCallオブジェクト

大事なことなので、記憶を裏打ちしてみる。

<!doctype html><html><head><title></title></head><body><script>

function fn() {
  alert(typeof a + ", " + a); // undefined, undefined
  alert(typeof b + ", " + b); // function, function() { alert("b2"); }
  b(); // alert("b2");
  try {
    alert(typeof c + ", " + c);
  } catch(err) { alert("c was not found"); }

  function b() {
    alert("b3");
  }

  function b() { // duplicity
    alert("b2");
  }

  var a = 3,
      b = function() { alert("b1"); }; // overwrite b

  alert(typeof a + ", " + a); // number, 3
  alert(typeof b + ", " + b); // function, function() { alert("b1"); }
  b(); // alert("b1");

  try {
    delete b;
  } catch(err) { alert("delete b; fail"); }

  return b;
}

alert("return " + fn()); // return function() { alert("b1"); }

</script></body></html>
  1. fn() を実行すると、Callオブジェクトが作成される
  2. 関数内の関数を全て検索しリストを作成する。リストには function b() { alert("b2)"; } が登録される。function b() { alert("b3"); } は b2 により存在が上書される。
  3. 関数内のvar文を全て検索しリストを作成する。リストには a, b が登録される
  4. Callオブジェクトに a と b が登録される。this や arguments も登録される
  5. Callオブジェクトの各プロパティが以下の値で初期化される
  6. var a = 3; の行に到達すると a は 3 で初期化される。それまでは undefined で初期化されている
  7. var b = function() { alert("b1"); } の行に到達すると、Call.b が 上書される
  8. Call.c は宣言されておらず、Globalオブジェクトにも存在しないため、参照エラーが発生する
  9. delete b; で Call.b は消せない。例外も発生しない(暗黙)
  10. return b; が返すのは function() { alert("b1") } オブジェクト関数を toString() した値が最後に表示される。

同じ関数が存在した場合は上書されてしまうから…

<!doctype html><html><head><title></title></head><body><script>

function fn(a) {
  if (a === 1) {
    function b() { alert("a === 1"); }
  } else {
    function b() { alert("a !== 1"); }
  }
  return b;
}

fn(1)();

</script></body></html>

これは、 alert("a !== 1") が実行される。



記憶を頼りにイメージで書いてるから、初期化処理の正確な手順は ECMAScript仕様書を見たほうがいいよ(Execution Contexts のとこ)

http://www2u.biglobe.ne.jp/~oz-07ams/prog/ecma262r3/ (ECMAScript3 日本語訳)

トラックバック - http://d.hatena.ne.jp/uupaa/20090701/1246414472