Object.setPrototypeOf を作る

https://mail.mozilla.org/pipermail/es-discuss/2013-March/029259.html で気付かされた。

Firefox なら Object.setPrototypeOf を作ることができる。

Firefox、というよりも、SpiderMonkey なのだが、Object.prototype.__proto__がgetter/setterで定義されている。

Object.getOwnPropertyDescriptor(Object.prototype, "__proto__");
/*
{
  configurable: true,
  enumerable: false,
  get: function () { [native code] },
  set: function () { [native code] }
}
*/

Firefox 17 からは、上記のようになっている。このDescriptorからsetを取り出してあげれば、待望のObject.setPrototypeOfを作れる。

(function() {
  var protoDesc = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__");
  if (typeof protoDesc.set === "function") {
    Object.defineProperty(Object, "setPrototypeOf", {
      value: Function.prototype.call.bind(protoDesc.set),
      configurable: true,
      writable: true
    });
    delete Object.prototype.__proto__;
  }
}());

Object.create(null)で作ったprototypeが無いオブジェクトにも安心してプロトタイプ設定が可能になるわけだ。もう、__proto__を使用する必要は皆無だね!

var obj = Object.create(null);
var proto = {
  hello: function() { return "hello world"; },
};

Object.setPrototype(obj, proto);

obj.hello();                          // => "hello world"
obj.hasOwnProperty("hello")           // => false
Object.getPrototypeOf(obj) === proto; // => true

素晴らしいですね。

Chrome (V8エンジン) では

GoogleChrome(27.0.1425.2 dev-mあたり) の __proto__ 挙動が変わった - hogehoge で V8エンジンもObject.prototype.__proto__が定義されるようになったけど、

{
  value: null,
  writable: true,
  enumerable: false,
  configurable: true
}

とgetter/setterになっていないため、Object.setPrototypeOfを作り出すことは不可能。