My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Thursday, May 16, 2013

Object.setPrototypeOf(O, proto) IS in ES6

This one is a short one, just to confirm that finally Object.setPrototypeOf(obj, proto) made it into ES6.
The section 15.2.3.2 speaks clearly:
15.2.3.2 Object.setPrototypeOf ( O, proto )
When the setPrototypeOf function is called with arguments O and proto, the following steps are taken:
  1. If Type(O) is not Object, then throw a TypeError exception.
  2. If Type(proto) is neither Object or Null, then throw a TypeError exception.
  3. Let status be the result of calling the [[SetInheritance]] internal method of O with argument proto.
  4. ReturnIfAbrupt(status).
  5. If status is false, then throw a TypeError exception.
  6. Return O.
Without going into details, the most basic polyfill woud be like this:
(function(O,s){
O[s]||(O[s]=function(o,p){o.__proto__=p;return o})
}(Object,'setPrototypeOf'));
If you want to go into details, the full reliable polyfill is hard to write down due all inconsistencies across JS engines out there where the __proto__ setter cannot be reused which means it's not possible to trust this magic over objects created from null.
All you need to do is forget __proto__ and use the suggested polyfill until the day __proto__ can simply disappear from those specs pages.
Enjoy!
This is a full polyfill with some extra info you might want to analyze, whenever ployfill is strictly true or false.
/*jslint devel: true, indent: 2 */
// 15.2.3.2
if (!Object.setPrototypeOf) {
Object.setPrototypeOf = (function(Object, magic) {
'use strict';
var set;
function checkArgs(O, proto) {
if (typeof O !== 'object' || O === null) {
throw new TypeError('can not set prototype on a non-object');
}
if (typeof proto !== 'object' && proto !== null) {
throw new TypeError('can only set prototype to an object or null');
}
}
function setPrototypeOf(O, proto) {
checkArgs(O, proto);
set.call(O, proto);
return O;
}
try {
// this works already in Firefox and Safari
set = Object.getOwnPropertyDescriptor(Object.prototype, magic).set;
set.call({}, null);
} catch (o_O) {
if (
// IE < 11 cannot be shimmed
Object.prototype !== {}[magic] ||
// neither can any browser that actually
// implemented __proto__ correctly
// (all but old V8 will return here)
{__proto__:null}.__proto__ === void 0
// this case means null objects cannot be passed
// through setPrototypeOf in a reliable way
// which means here a **Sham** is needed instead
) {
return;
}
// nodejs 0.8 and 0.10 are (buggy and..) fine here
// probably Chrome or some old Mobile stock browser
set = function(proto) {
this[magic] = proto;
};
// please note that this will **not** work
// in those browsers that do not inherit
// __proto__ by mistake from Object.prototype
// in these cases we should probably throw an error
// or at least be informed about the issue
setPrototypeOf.polyfill = setPrototypeOf(
setPrototypeOf({}, null),
Object.prototype
) instanceof Object;
// setPrototypeOf.polyfill === true means it works as meant
// setPrototypeOf.polyfill === false means it's not 100% reliable
// setPrototypeOf.polyfill === undefined
// or
// setPrototypeOf.polyfill == null means it's not a polyfill
// which means it works as expected
// we can even delete Object.prototype.__proto__;
}
return setPrototypeOf;
}(Object, '__proto__'));
}