__proto__ = null をした後の __proto__ = {} について

記事とは関係ない質問なのですが
__proto__にnullを設定した場合のみ
以降のinstanceof演算子の挙動が変わるのは仕様なのでしょうか
FirefoxChromeで確認しました

o = {}
o.__proto__ = Object.prototype
o instanceof Object //true

o.__proto__ = {}
o.__proto__ = Object.prototype
o instanceof Object //true

o.__proto__ = 'abc'
o.__proto__ = Object.prototype
o instanceof Object //true

o.__proto__ = undefined
o.__proto__ = Object.prototype
o instanceof Object //true

o.__proto__ = null
o.__proto__ = Object.prototype
o instanceof Object //false

o.__proto__ = {}
o.__proto__ = Object.prototype
o instanceof Object //false
http://d.hatena.ne.jp/teramako/20130713/p2#c1374302690

ちょっと面白い質問を受けました。

__proto__ の仕様

ECMAScript 6th から __proto__ が付録として仕様が定義されています。

Annex B.2.2.1 あたりに定義されていますが、Object.prototype.__proto__ として存在します。
大抵のオブジェクトが Object.ptototype をプロトタイプチェーンに持つため、obj.__proto__ で値を得たり代入したりができるという仕組みです。

大事なことなので二回言いますが、"__proto__" は Object.prototype にあります。

逆にいうと、プロトタイプチェーンに Object.prototype を持たないオブジェクトは、"__proto__" という特殊なプロパティを持たないことになります。

__proto__ への代入

上記、仕様リンク先に挙動が載っていますが、__proto__ への代入で意図する挙動となるには、代入する値が、Object または Null である必要があります。

仕様通りなら、Object または null 以外の代入は TypeError が発生するはずですが、現状の Chrome, Firefox では発生せずに無効扱いになるようです。

instanceof 演算子

instanceof 演算子は、左辺の[[Prototype]]と右辺のprototypeプロパティが一致するかを比べる演算子です。

Object.getPrototypeOf(o) === Constructor.prototypeと同様の結果を返すことになります。

問題のコード

o = {}
o.__proto__ = Object.prototype
o instanceof Object //true

まぁ、ここはいいでしょう。

o.__proto__ = 'abc'
o.__proto__ = Object.prototype
o instanceof Object //true

o.__proto__ = undefined
o.__proto__ = Object.prototype
o instanceof Object //true

代入する'abc'undefinedは、Object または null ではないので、無効です。[[Prototype]] は変更されません。

o.__proto__ = null
o.__proto__ = Object.prototype
o instanceof Object //false

ここが問題です。
null を代入すると、[[Prototype]] が null に設定されます。これはプロトタイプチェーンに Object.prototype を持たないということを意味します。

そして、instanceof は null === Object.prototype としたような状態となり、false となります。

o.__proto__ = {}
o.__proto__ = Object.prototype
o instanceof Object //false

さて、前のコードで、変数oの[[Prototype]]は null になっています。Object.prototype.__proto__へは結びつかない状態です。
この状態の __proto__ は普通のプロパティであり、特殊なものではなくなっています。

つまり、o.prop = {} とした時と同じ扱いです。o.__proto__ = {} は[[Prototype]]への設定ではなく、oの"__proto__"プロパティへ値の設置として働きます。
[[Prototype]]は変更されませんので、instanceof 演算も false を返すままということになります。

ということで、仕様通りです。