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__'));
}

3 comments:

Anonymous said...

Andrea, big thanks for you activity to give this issue such attention.

It's totally awesome we finally have Object.setPrototypeOf instead of ugly obj.__proto__

Sebastian said...

I don't get why the hate of __proto__, and want to remove it from the spec. I find it clear and easy to use. I thought it was going to be standardised in ES6. Personally I prefer o.__proto__ = x than Object.setPrototypeOf(o, x)

Andrea Giammarchi said...

no Sebastian, you don't prefer that, trust me.

You don't want a property name able to destroy the nature of an object in a loop because you didn't filter that.

You don't want to be unable to use dictionaries in a clean way where every property is a own one and no magic is generated by any other.

You don't want to pollute the Object.prototype with proprietary experiments only because of early, and broken, implementations, plus you don't want to standardize a completely broken/messed up property you cannot polyfill across already broken implementations.