5 Reasons You Should Avoid __proto__
Update: when you've done with this post, there's even more in comments and the newer one: Yet Another Reason To Drop __proto__
Too many discussions without real outcome about
TC39 refused to standardize this property because of the amount of problems it brings. Apparently will be part of ES6 but is not there yet.
As example, try this in some environment:
Having a property configurable means you cannot trust it's going to work because anyone could have decided to remove it ... why ...
Using
The extra silent agreement here is that
Bad news is: you cannot redefine
Polymorphism has always been possible through mixins and borrowed methods but again, no way an object could suddenly be any sort of instance with such lightweight casting unable to ensure any desired behavior.
What I mean, is that using
This is different from
Here some advantage:
Too many discussions without real outcome about
__proto__
magic evilness or feature. It's time to understand why using it is, today, a bad idea.__proto__ Is NOT Standard (yet)
__proto__
is a silent, non spec'd, agreement. What's the problem? Keep reading ;)__proto__ Could NOT Be There
The silent non standard agreement sees__proto__
as configurable property of the Object.prototype
.As example, try this in some environment:
The outcome is migrating from
(this.alert || console.warn)(
delete Object.prototype.__proto__
); // false or true ?
false
to true
. As example, current Chrome has a non configurable descriptor, while Canary has a configurable one. Same is for latest node.js, Firefox, and who know who else.Having a property configurable means you cannot trust it's going to work because anyone could have decided to remove it ... why ...
__proto__ Makes null Objects Unpredictables
Theoretically, this is all you need to create one or more objects that inherits fromnull
We cannot trust, as example in Chrome, that any property can be set there because if for some reason the property name is
var Dict = Object.create.bind(Object, null);
var dictionary = Dict();
dictionary[property] = 1;
if (otherProperty in dictionary) {
// do stuff
}
// or
if (dictionary[someOtherThing]) {
// do stuff
}
__proto__
, that object inheriting null will completely be unusable/screwed.Using
hasOwnPropertyDescriptor()
is not even an option since objects that inherit from null do not inherit these methods from Object.prototype
.The extra silent agreement here is that
Object.create(null)
should return objects that are not affected anyhow by any property from Object.prototype
and __proto__
ain't any exception!__proto__ Is NOT Secure
Since any bloody object could be modified directly or deeply anywhere in the middle of its prototypal chain, you can consider all your instances somehow exposed to any sort of attack.Bad news is: you cannot redefine
__proto__
to prevent this, at least you cannot in current version of Chrome and mobile browsers: Error: cannot redefine property __proto__
function ProtoSafe() {}
Object.defineProperty(
ProtoSafe.prototype,
'__proto__',
{
get: function () {
return ProtoSafe.prototype;
},
set: function () {
throw 'immutable';
}
}
);
__proto__ Influences Your Logic
This is last point but actually still important. In ES3 it has never been possible to redefine inheritance over an already created instance and this has never been a real problem.Polymorphism has always been possible through mixins and borrowed methods but again, no way an object could suddenly be any sort of instance with such lightweight casting unable to ensure any desired behavior.
What I mean, is that using
[].slice.call(arguments)
has a meaning: if there is a length
in the used object, create an array with indexes filled from 0 to that length - 1.This is different from
generic.__proto__ = Array.prototype;
because the length
could be missing and the resulted object behavior be unexpected, broken, or again unpredictable.Still A Cheap Way To Cast
This is the only advantage and the reason some library adopted__proto__
without even thinking about it: performance boost, compared to a whole slice, are a win, and specially in mobile and DOM libraries where results are always threat as ArrayLike objects.Object.setPrototypeOf(object, proto) To The Rescue!
In ES5 allObject
things are managed through the Object
constructor so why not having a method in charge of the __proto__
behavior, since Object.getPrototypeOf(object)
is already available?Here some advantage:
- no way a property can destroy an object, the
obj[propName]
check againstpropName !== '__proto__'
won't be needed anymore: performance! - cheap casting still available so not a performance issue
- standardizing
Object.setPrototypeOf(object, proto)
can bring new possibilities suchObject.freezePrototype(object)
in order to ensure immutable inheritance when and if needed
A Cheaper Example
If you want to ensure TC39 will ever consider to drop this property in favor of better methods in theObject
, here what you should stick in your library that is using proto: It's cheap and performance, again, are as good as before!
var setPrototypeOf = Object.setPrototypeOf || function(o, p){
o.__proto__ = p;
return o;
};
Comments
Post a Comment