メソッド実行時の引数に関数代入の挙動

不思議な挙動だね。

Firefox 8.0

var o = {};
o.hoge(o.hoge = function(){ console.log("hoge"); });
// TypeError: o.hoge is not function
o.hoge();
// "hoge"
o.hoge(o.hoge = function(){ console.log("HUGA"); });
// "hoge"

Opera 11.52

var o = {};
o.hoge(o.hoge = function(){ console.log("hoge"); });
// TypeError: o.hoge is not function
o.hoge();
// "hoge"
o.hoge(o.hoge = function(){ console.log("HUGA"); });
// "hoge"

GoogleChrome 17.0.938.0 dev-m

var o = {};
o.hoge(o.hoge = function(){ console.log("hoge"); });
// "hoge"
o.hoge();
// "hoge"
o.hoge(o.hoge = function(){ console.log("HUGA"); });
// "HUGA"

GoogleChromeはメソッド実行前に、o.hogeに関数が代入されていて、それが即実行される。

Firefox, Operaは代入前にcallできるかチェックしているみたいで、例外を投げるけど、引数内の式は実行されている。

どういうことなの?

追記

  1. Let ref be the result of evaluating MemberExpression.

  2. Let func be GetValue(ref).

  3. Let argList be the result of evaluating Arguments, producing an internal list of argument values (see 11.2.4).

  4. If Type(func) is not Object, throw a TypeError exception.

  5. If IsCallable(func) is false, throw a TypeError exception.

  6. If Type(ref) is Reference, then

    1. If IsPropertyReference(ref) is true, then

      1. Let thisValue be GetBase(ref).

    2. Else, the base of ref is an Environment Record

      1. Let thisValue be the result of calling the ImplicitThisValue concrete method of GetBase(ref).

  7. Else, Type(ref) is not Reference.

    1. Let thisValue be undefined.

  8. Return the result of calling the [[Call]] internal method on func, providing thisValue as the this value and providing the list argList as the argument values.

  1. 最初にメソッドのオブジェクトを取って(func)
  2. 次に引数を得る
  3. funcはObjectではないので例外を投げる
    • o.hoge === undefined時はObjectではないので例外を投げる
  4. 最後にfuncをcallする
    • funcは代入前のものなので引数で代入したものではない

という挙動が仕様みたいだから、Firefox, Operaの挙動が正しいように思える。