Firefox4 と Chrome, Safari の ES5 実装

Hello,
I discovered that the following ES5 code evaluates differently in FF4
than Chrome and Safari.

function Ctor() {};
Object.defineProperty(Ctor.prototype, "prop", {
        value: "prototype",
        writable: false
});

var obj = new Ctor();
obj.prop = "own";
alert(obj.prop + ", " + obj.hasOwnProperty("prop"));

Chrome and Safari gives: "own, true".
FF4 gives: "prototype, false".

Could anybody explain why?

greetings
Michal

[JSMentors] ES5 prototype and property descriptor

面白いなと思ったので紹介。
Ctorのプロトタイプにwritable: falseでプロパティをセットしてインスタンスを作り、自身のプロパティ(OwnProperty)にプロトタイプ内にあるプロパティと同名のプロパティをセットしようとした時の挙動。

Firefox4ではOwnPropertyはプット([[Put]])されないが、Chrome,Safariではプットされるというもの。

おや?と思って仕様(ECMA-262)を見てみた。

[[CanPut]]内部関数

問題は、obj.prop = "own";の部分。
仕様の流れを追うと

  1. 11.13.1 Simple Assignment (=)
    • LeftHandSideExpression = AssignmentExpression
    • 幾つかチェックした後、PutValue関数をコール
  2. 8.7.2 PutValue (V, W)
    • 幾つかチェックした後、[[Put]]内部関数をコール
  3. 8.12.5 [[Put]]内部関数
    • [[CanPut]]をコールし、返り値resultを得る
      • resultがfalseである場合は値を設定しない(場合によっては例外を出す)
  4. 8.12.4 [[CanPut]]内部関数
    1. [[GetOwnProperty]]でプロパティの設定を得る
      • obj.propは未定義なので undefined
      • また、対象オブジェクトは[[Extensible]]である
    2. 仕方ないので、プロトタイプをさかのぼる
      1. 得たプロトタイプオブジェクトに対して[[GetOwnProperty]]を呼ぶ
      2. 得られた設定はDataDescriptorである
      3. 設定値を得て見てみると[[Writable]]がfalseなのでfalseを返す

というプロセスを経ると思われる。

[[CanPut]]内部関数が false を返すので、値は設定されないはず。ということで、Firefox4の挙動が正しいように思う。
なんか直感的ではない気がするので、Chrome,Safariではこの[[CanPut]]内部関数の処理をわざときちんと実装していないんじゃないかなと思う。

っていう理解で合ってますか? < ECMAScript 詳しい人

Object.defineProperty

では、Object.defineProperty(obj, "prop", { value: "own" })をした場合はどうなる?

  1. 15.2.3.6 Object.defineProperty
    • [[DefineOwnProperty]]内部関数をコール
  2. 8.12.9 [[DefineOwnProperty]]
    • プロトタイプ側は関係ないし、[[CanPut]]内部関数はコールしない

よって、これは Firefox4 でもオブジェクト自身にプロパティを設置できる。