Object.setPrototypeOf(obj, proto)
made it into ES6.The section 15.2.3.2 speaks clearly:
15.2.3.2 Object.setPrototypeOf ( O, proto )Without going into details, the most basic polyfill woud be like this:
When thesetPrototypeOf
function is called with arguments O and proto, the following steps are taken:
- If Type(O) is not Object, then throw a TypeError exception.
- If Type(proto) is neither Object or Null, then throw a TypeError exception.
- Let status be the result of calling the [[SetInheritance]] internal method of O with argument proto.
- ReturnIfAbrupt(status).
- If status is false, then throw a TypeError exception.
- Return O.
(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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*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:
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__
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)
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.
Post a Comment