__proto__プロパティの不思議
__proto__
プロパティ周りでJavaScript実装によっていろいろ挙動が異なることが分かったのでメモ。
以下のブラウザ(エンジン)で調べた
- Firefox 9.0.1 (Gecko 20111220165912)
- GoogleChrome 17.0.963.12 dev-m (v8 ???)
- Opera 12.00 alpha (Presto 2.10.238)
また、GoogleChromeとNode.jsでの結果は同じだった。
var bind = Function.prototype.bind, hasOwn = bind.bind(bind.call)(Object.prototype.hasOwnProperty), proto = Object.getPrototypeOf;
Object.prototype の __proto__
Code | Firefox | GoogleChrome | Opera |
---|---|---|---|
hasOwn(Object.prototype, "__proto__") | true | false | false |
"__proto__" in Object.prototype | true | true | true |
desc(Object.prototype, "__proto__") | [Object object] | undefined | undefined |
Object.prototype.__proto__ | null | null | null |
GoogleChrome, Opera の __proto__ は in 演算子では存在することになっているが、どこにあるん?
Firefox も不思議で、デスクリプタとしては({ configurable:false, enumerable:false, value:(void 0), writable:true })
なのに、null
が返ってきたり...
普通のオブジェクト
var o = {};
Code | Firefox | GoogleChrome | Opera |
---|---|---|---|
"__proto__" in o | true | true | true |
hasOwn(o, "__proto__") | false | false | false |
hasOwn(proto(o), "__proto__") | true | false | false |
Firefoxだけ少し違う結果だが、これはObject.prototype
の特性がそのまま出ている。
defineProperty による __proto__ 定義(普通のオブジェクト編)
oに__proto__
プロパティを定義してみよう。
Object.defineProperty(o, "__proto__", { value: { foo: "FOO" } });
Code | Firefox | GoogleChrome | Opera |
---|---|---|---|
hasOwn(o, "__proto__") | true | true | true |
o.__proto__ === Object.prototype | false | true | false |
proto(o) === Object.prototype | true | true | true |
o.__proto__.foo | "FOO" | undefined | "FOO" |
GoogleChromeが変。後述するけど、defineProperty
で__proto__
を定義しようとしても無視されるっぽい。
プロトタイプを継承しないオブジェクト
ES5 からプロトタイプを持たない素のオブジェクトが作成できる。こちらで試してみよう。
var o2 = Object.create(null);
Code | Firefox | GoogleChrome | Opera |
---|---|---|---|
"__proto__" in o2 | false | true | true |
o2.__proto__ | undefined | null | null |
proto(o2) | null | null | null |
FirefoxにはObject.prototype
に__proto__
があり、それが継承されないオブジェクトと考えると正しい結果となっているように見える。が、GoogleChroe, Opera では、in演算子では存在し、__proto__
プロパティもnull
を返す結果となる。
__proto__プロパティの代入
o2.__proto__ = { foo: "FOO" };
Code | Firefox | GoogleChrome | Opera |
---|---|---|---|
o2.foo | undefined | "FOO" | "FOO" |
hasOwn(o2, "__proto__") | true | false | false |
o2.__proto__.foo | "FOO" | "FOO" | "FOO" |
proto(o2).foo | TypeError: proto(o2) is null | "FOO" | "FOO" |
Firefoxでは__proto__
が普通のオブジェクトとして定義され、GoogleChrome, Operaでは[[Prototype]]としてプロトタイプに定義される結果となる。
defineProperty による __proto__ 定義
var o3 = Object.create(null); Object.defineProperty(o3, "__proto__", { value: { hoge: "HOGE" } });
Code | Firefox | GoogleChrome | Opera |
---|---|---|---|
"__proto__" in o3 | true | true | true |
hasOwn(o3, "__proto__") | true | true | true |
o3.__proto__; | ({hoge: "HOGE"}) | null | ({hoge: "HOGE"}) |
GoogleChromeではdefinePropertyで__proto__
はできない(エラーにもならない)。
まとめ(?)
- Firefoxでは
Object.prototype
に__proto__
があり、これが[[Prototype]]へのgetter/setterの様な役割を担っているObject.prototype
をプロトタイプチェーンに持たないオブジェクトでは__proto__
によるプロトタイプ代入は不可
- GoogleChrome, Opera では、in演算子が変。[[Prototype]]があろうが無かろうが
true
を返す - definePropertyで
__proto__
をやると、GoogleChromeではブラックホールに呑まれる- [[Prototype]]とはならない、
__proto__
は定義できないようになっていると思われる
- [[Prototype]]とはならない、