Readdition of __proto__
To search es-discuss, I use "site:mail.mozilla.org es-discuss" as first two terms in a Google search. Sometimes I just use "es-discuss". If you add "__proto__" you'll find lots to read. Add "meeting notes" and you'll find recorded TC39 decisions.
I usually find links and include them here to avoid just saying "do your own search", but I'm short on time, sorry!
Thanks. Found a bunch of interesting things. Hopefully I'll find some use case where it doesn't completely break the beautiful OOP behavioral typing gives us when we play nice with it (Yep, that dog of yours, he's no longer an animal) :)
Yes, it's hard to search in this mailing list but luckily not everyone in here will tell you not to be "that guy that clearly didn't read anything and is just annoying" ^_^
Since I've personally pushed to drop __proto__
I might be the right
person to give you pros and cons.
Feel free to ask me more, if necessary
(off-topic: the double underscore to talk about dunder proto is a hilarious must)
Pros
- some library early adopted
__proto__
and few others followed making a de facto used approach to easily hot/swap inehritance - it's widely available across all mobile and modern desktop browsers
- it's faster than
Array.prototype.slice.call(genericCollection)
which is the unique place where Zepto library, probably the main reason dunder is still here, decided to swapquerySelectorAll
results ignoring incompatibility with IE Mobile at that time not so popular (then, one of the reason IE11 wanted to adopt it)
Cons
- due lack of proper specs, every browser implemented such property in a slightly different way creating a security and behavior mess across all browsers
- due previous point,
Object.create(null)
may not be safe dictionaries since in some case adding a key__proto__
will change the object inheritance instead of adding just the keyword proto - ES5 introduced
Object.getPrototypeOf
but notObject.setPrototypeOf
which would ensure an explicit intent over the generic option instead of being a "bomb" keyword any object could potentially suffer for
Current Status
Instead of formalizing its form, ES6 accepted Object.setPrototypeOf
as
described in specs and decided to silently move beside, but still have in
specs, the dunder __proto__
form, fixing at least a couple of related
gotchas so that:
- unless explicitly set as property,
__proto__
is a named property for every object that should not affect inheritance so thatobj["__proto__"]
orobj[key]
wherekey
is the string"__proto__"
should not hot/swap the prototypal chain obj.__proto__
is still valid when explicit, it should fail withObject.create(null)
object but it should be a quick shortcut to create inheritance too suchvar obj = {__proto__: null}
keeping compatibility but avoiding troubles withJSON
serialized objects where once again{"__proto__":[]}
will behave as named property instead of prototype mutator.
I hope these answers are OK and to all others, please correct me whenever you think whatever I've written is wrong.
Best , Andrea Giammarchi
On Mon, Oct 14, 2013 at 1:15 PM, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:
Yes, it's hard to search in this mailing list but luckily not everyone in here will tell you not to be "that guy that clearly didn't read anything and is just annoying" ^_^
Brendan, since you participated, above cit. wasn't at all directed to you!
Anyway, yes, I still believe this ML is not as easy to lurk into as a forum would be plus TC39 meetings are huge and often dispersive.
I hope my reply answered/summarized properly and quickly few major discussed points.
Andrea Giammarchi wrote:
Current Status
(Thanks for the dunders! :-P)
Instead of formalizing its form, ES6 accepted
Object.setPrototypeOf
as described in specs and decided to silently move beside, but still have in specs, the dunder__proto__
form, fixing at least a couple of related gotchas so that:
- unless explicitly set as property,
__proto__
is a named property for every object that should not affect inheritance so thatobj["__proto__"]
orobj[key]
wherekey
is the string"__proto__"
should not hot/swap the prototypal chain
I'm not sure what you mean here, but first, __proto__
is not specified
in ES6 drafts as an own property. See
people.mozilla.org/~jorendorff/es6-draft.html#sec-B.2.2.1
B.2.2.1 Object.prototype.__proto__
Object.prototype.__proto__ is an accessor property with attributes { [[Enumerable]]: false, [[Configurable]]: true }. The [[Get]] and [[Set]] attributes are defined as follows
B.2.2.1.1 get Object.prototype.__proto__
The value of the [[Get]] attribute is a built-in function that requires
no arguments. It performs the following steps:
1. Let O be the result of calling ToObject the *this* value as the argument.
2. ReturnIfAbrupt(O).
3. Return the result of calling the [[GetPrototypeOf]] internal method of O.
B.2.2.1.2 set Object.prototype.__proto__
The value of the [[Set]] attribute is a built-in function that takes an argument proto. It performs the following steps:
1. Let O be CheckObjectCoercible(this value).
2. ReturnIfAbrupt(O).
3. If Type(proto) is neither Object or Null, then return proto.
4. If Type(O) is not Object, then return proto.
5. Let status be the result of calling the [[SetPrototypeOf]] internal method of O with argument proto.
6. ReturnIfAbrupt(status).
7. If status is false, then throw a TypeError exception.
8. Return proto.
Andrea Giammarchi wrote:
Brendan, since you participated, above cit. wasn't at all directed to you!
I didn't think so, since I've patiently and at some cost dug for links when people ask, e.g. in reply to you, here:
:-|.
I meant that IIRC obj["__proto__"]
should not invoke that Annex B
specified getter (@Benjamin, Annex B is where you'll find everything
related indeed) but obj.__proto__
will ... unless once again I've missed
some update.
Yeah, I know that you link stuff, and glad you made some time for extra clarification.
Thanks ! This is really above and beyond what I could have asked for. I really wish there was an easy way to search the list like tags. I've been reading for a while but when I want to bring up, learn more about or discuss things - not being able to search really sucks. It makes me want to avoid things because I know how frustrating repetition can be.
My opinion is probably a bit radical (not sure how many people here agree with it), feel free to skip it as it's probably more of a rant :P
No, honestly - don't read it. It's repeating things that have probably been said here before but I couldn't find (well, other than Branden here esdiscuss/2010-April/010917 ).
- it's widely available across all mobile and modern desktop browsers
So is with
but that still doesn't work in strict mode. I don't see why
having a feature people agree is bad (at least in the way it's currently
used) makes adding it for a standard a good idea. You can standardize it
and still not require browsers to implement it. If you choose to implement
proto then... .
it's faster than
Array.prototype.slice.call(genericCollection)
I hope this is not really an argument people are considering for adding
proto to the spec. An optimization for
Array.prototype.slice.call(genericCollection)
can be done instead on
vendor level.
- some library early adopted
__proto__
and few others followed making
a de facto used approach to easily hot/swap inehritance
This is the most problematic argument here in my opinion. We have this beautiful prototypical inheritance system in JavaScript that lets us write clean and expressive code by not pretending we have some half working type system and instead embracing behavioral typing. It's really great and I really like it.
This behavioral typing system entails that we act sensible about our types, inheritance can really work nicely and if I keep to the LSP and other common design principles I find that my code in JavaScript is a lot more expressive than similar OOP code in structurally typed languages.
Being able to mutate proto breaks this. It breaks it bad. Suddenly, my
Dog
stops being an Animal
or my Car
stops being a Vehicle
. This
makes no sense, why would we enable people to break behavioral typing like
this? This is just like the delete
operator.
The object creation case and getPrototypeOf make a lot of sense but mutating proto after an object has been created sounds like a horrible broken approach that shoots behavioral typing in the foot. Especially when I'm removing stuff from the prototype of an object.
Sorry for the long rant if you've gotten this far.
Andrea Giammarchi <mailto:andrea.giammarchi at gmail.com> October 14, 2013 1:32 PM
I meant that IIRC
obj["__proto__"]
should not invoke that Annex B specified getter (@Benjamin, Annex B is where you'll find everything related indeed) butobj.__proto__
will ... unless once again I've missed some update.
What you just wrote is not true of __proto__
in SpiderMonkey or other
engines I can test (V8, JSC).
It also can't be true via ES6 Annex B, since obj[key]
and obj.foo
where
key = 'foo'
both lookup prototype properties and will find
Object.prototype.foo
.
Benjamin (Inglor) Gruenbaum <mailto:inglor at gmail.com> October 14, 2013 1:33 PM Thanks ! This is really above and beyond what I could have asked for.
Beware that what Andreas wrote about proto in ES6 was not accurate.
I really wish there was an easy way to search the list like tags. I've been reading for a while but when I want to bring up, learn more about or discuss things - not being able to search really sucks. It makes me want to avoid things because I know how frustrating repetition can be.
There is also esdiscuss.org -- give it a try. Follow @esdiscuss (Domenic runs it) on twitter too!
My opinion is probably a bit radical (not sure how many people here agree with it), feel free to skip it as it's probably more of a rant :P
No, honestly - don't read it. It's repeating things that have probably been said here before but I couldn't find (well, other than Branden
"Brendan"
here esdiscuss/2010-April/010917 ).
- it's widely available across all mobile and modern desktop browsers
So is
with
but that still doesn't work in strict mode.
Yes, but (or perhaps "and in part, therefore") too few user strict mode.
You are talking about apples to oranges. Excluding 'with' from ES5 strict leaves 'with' in the default-mode language. ES6 is not a new "mode" (1JS). We cannot make proto go away by turning a blind eye toward it.
Our duty as a standards body includes specifying de-facto standards which browsers must implement to interop. proto is one such.
You are breaking an opened door with me and I've indeed pushed to not
have __dunder__
specd but, for how much remote anger I still have against
some decision some famous library and its main author made few months ago
...
- web developers and specially library authors are usually more pragmatic than rational ... they solve problems. If something is widely adopted and "works" they won't bother much with philosophy, I am sure you know what I mean
- performance is always a good argument. I agree that if everything is
screwed and slow, using
__proto__
instead ofslice
orpush
over a new instance won't give you much (the whole story in Zepto is that the collection has Zepto methods inherited so is not just slice but yeah, it could have been done differently for sure) - JavaScript is better than just OOP, it uses prototype but also it's
able to borrow methods from everything and everywhere (mixin). Hot swapping
the proto might be an easy, simple, fast, shortcut to promote, downgrade,
or change state in a state-machine system with a single operation. In JS
you can composite deleting and adding new "methods" (aka: invokable
properties) so if you ave a similar approach you might want to opt for
__dunder__
Once again, I've been fighting to not have it in for some similar reason and other security concerns plus it has been discussed infinite times and I believe everyone participated.
The current status is the best option for everyone hoping whatever library decides to hot/swap inheritance will do it with its own known objects/instances instead of anything that goes through that library.
In such/latter case, I would simply ignore that library as too greedy.
Right now:
- zepto and others will keep using
__proto__
going dirty - you can avoid all of this without problems
- others will forget about this with new ES6 Class syntax
Annex B is also an indicator that such feature is secondary so it's not something JS engines should absolutely have (these will in any case)
Best
Then I might have confused what decided with JSON
serialization where
"__proto__"
will be a property and not a setter, neither a getter once
deserialized.
Is this correct? Yeah, I remember that different accessors looked weird to me too ... thanks for clarification.
Best
From: es-discuss [es-discuss-bounces at mozilla.org] on behalf of Brendan Eich [brendan at mozilla.com]
Our duty as a standards body includes specifying de-facto standards which browsers must implement to interop. proto is one such.
It's worth highlighting this aspect of the situation. This duty of standards bodies has, at least from what I can see from my limited vantage point, only recently become apparent. But it's a crucial part of being a standards body today.
Relevant reading, from the dawn of time when this was becoming apparent in the HTML/web applications space (2004): ln.hixie.ch/?count=1&start=1085764602
(I look forward to people with more experience than me coming by to tell me about how this is not as new-fangled a paradigm as I think it is :)
JSON
serialization <= JSON
parse
From "Rationale for American National Standard for Information Systems – Programming Language – C", apparently in 1988:
The Committee’s overall goal was to develop a clear, consistent, and unambiguous Standard for the C programming language which codifies the common, existing definition of C and which promotes the portability of user programs across C language environments. The X3J11 charter clearly mandates the Committee to codify common existing practice. The Committee has held fast to precedent wherever this was clear and unambiguous.
On Mon, Oct 14, 2013 at 4:47 PM, Brendan Eich <brendan at mozilla.com> wrote:
You are talking about apples to oranges. Excluding
with
from ES5 strict leaveswith
in the default-mode language. ES6 is not a new "mode" (1JS). We cannot make__proto__
go away by turning a blind eye toward it.Our duty as a standards body includes specifying de-facto standards which browsers must implement to interop.
__proto__
is one such.
Do you mean more then inclusion in Annex B? The committee and community made the right move to go with Object.setPrototypeOf()
.
Rick Waldron wrote:
Do you mean more then inclusion in Annex B? The committee and community made the right move to go with
Object.setPrototypeOf()
.
Indeed, we reached a happy place with Object.setPrototypeOf
in the
normative spec, and Annex B specifying __proto__
for those JS embeddings
that need it for interop.
I'm not sure why you think I'm dissenting from any of that. My reply was
to correct a misstatement about __proto__
, and only that. Context!
I think you may have mixed up a few things:
-
JSON does not recognize
'__proto__'
per its unchanging spec, and so parsing that identifier makes an own data property. -
var obj = {__proto__: proto};
is a special form, unlike any other identifier__proto__
as the literal property name does assign (set) not a define. -
Annex B.2.2.1 defines an accessor on
Object.prototype
, which if unshadowed will be got or set when used by name on objects that actually delegate toObject.prototype
. It's also delete-able (configurable), important for SES and the like.
my last memories on the topic are these:
var obj = JSON.parse('{"__proto__":[]}');
obj instanceof Array; // false
obj.__proto__ instanceof Array; // true
// since the proto is a property, not a magic thing
obj.__proto__ = 123; // da hell will happen, only Allen knows ^_^
And since latter should simply set the property named "__proto__"
as
value 123
I got confused with this dual way to deal with an object when
it comes from JSON world (then has a property defined as value, not the
inherited set/get from Object.prototype)
As summary, JSON.parse
over "__proto__"
is similar to:
var o = {};
Object.defineProperty(o, '__proto__', {
value: 123,
enumerable: true,
writable: true,
configurable: true
});
Which means in such case the property "__proto__"
will fail with such
object while Object.setPrototypeOf
won't which is the reason I keep
suggesting the latest to make the intent explicit.
Not arguing or anything, just speaking out loudly my confusion with that property as string part.
Best
On Mon, Oct 14, 2013 at 5:22 PM, Brendan Eich <brendan at mozilla.com> wrote:
Indeed, we reached a happy place with
Object.setPrototypeOf
in the normative spec, and Annex B specifying__proto__
for those JS embeddings that need it for interop.I'm not sure why you think I'm dissenting from any of that.
I read through the entire thread and the message I was responding to read
as though you meant that the committee would push forward on __proto__
(normative, non-annex) for the greater good of browser interop. It appears
I may have read too much into your statement and I apologize for the
confusion my response created.
On Oct 14, 2013, at 4:21 PM, Andrea Giammarchi wrote:
my last memories on the topic are these:
var obj = JSON.parse('{"__proto__":[]}'); obj instanceof Array; // false obj.__proto__ instanceof Array; // true // since the proto is a property, not a magic thing obj.__proto__ = 123; // da hell will happen, only Allen knows ^_^
And since latter should simply set the property named
"__proto__"
as value123
I got confused with this dual way to deal with an object when it comes from JSON world (then has a property defined as value, not the inherited set/get from Object.prototype)As summary,
JSON.parse
over"__proto__"
is similar to:
var o = {}; Object.defineProperty(o, '__proto__', { value: 123, enumerable: true, writable: true, configurable: true });
Which means in such case the property
"__proto__"
will fail with such object whileObject.setPrototypeOf
won't which is the reason I keep suggesting the latest to make the intent explicit.Not arguing or anything, just speaking out loudly my confusion with that property as string part.
I think you are over thinking this:
Assuming that Annex B.2.2.1 and B.3.1 are implemented: here are the cases of interest:
let o1 = {__proto__: p}; // o1 inherits from p
let o2 = {"__proto__": p}; // o2 inherits from p
let o3 = {["__proto__"]: p};// o3 inherits from Object.prototype, has own data property "__proto__" whose value is p.
let o4 = JSON.parse('{"__proto__": "value"}'); //o4 inherits from Object.prototype, has own data property "__proto__" whose value is "value"
//assuming that Object.prototype.__proto__ has not been tamper with:
let o5 = new Object;
o5.__proto__ = p ; //o5 inherits from p
let o6 =new Object;
o6["__proto__"] = p; //o6 inherits from p
Allen my confusion is with o4 ... what happens once you re-set/assign its
__proto__
there?
Is it just a normal property so new value will be set ? Is it a magic inherited thing (it shouldn't) that will change the o4 prototype chain ?
On Oct 14, 2013, at 5:54 PM, Andrea Giammarchi wrote:
Allen my confusion is with o4 ... what happens once you re-set/assign its
__proto__
there?Is it just a normal property so new value will be set ? Is it a magic inherited thing (it shouldn't) that will change the o4 prototype chain ?
JSON.parse is just a function that builds up simple (inheriting from Object.prototype
) objects using [[DefineOwnProperty]] See people.mozilla.org/~jorendorff/es6-draft.html#sec-24.3.2 step 3.a.iv.3.a. So if the JSON object text contains a "__proto__"
property key, the object that is created with have a "__proto__"
own data property. If it doesn't then the Annex B2.2.1 Object.prototype__proto__
property is inherited. No magic involved.
we are lucky enough there's no browser without a native JSON object that uses the D. Crockford polyfill ^_^
(early sent) I meant passing through the prototype. The "unmagic" behavior
is when you deal with such object thinking accessing its __proto__
will
behave like others. So it's the other way round but again, I know all of
this, I was just confused by it and explained indeed with an example code
how it is simulating the Object.defineProperty
Thanks anyway for clarifications.
There seems to be an interesting case missing:
let attr = "__proto__";
let o7 = new Object;
o7[attr] = p; // is this like o3?
Not resolving this like o3 (or o6 really) sounds very strange. I think:
let attr = "__proto__";
let o7 = new Object;
o7[attr] = p; // o7 inherits from p
Is the correct behavior here (why would it not invoke the setter?)
This case is exactly the same as O6. Perhaps I should have written O6 as:
o6[ ("__proto__") ] = p; //o6 inherits from p
to make that clearer.
Benjamin (Inglor) Gruenbaum wrote:
Not resolving this like o3 (or o6 really) sounds very strange. I think:
let attr = "__proto__"; let o7 = new Object; o7[attr] = p; // o7 inherits from p
Is the correct behavior here (why would it not invoke the setter?)
Allen confirmed, but just to be clear, any world where o["foo"]
and do { let key = "foo"; o[key]; }
(do-expression syntax from harmony-era strawman) differ is crazytown, and we do not go there.
On Tue, Oct 15, 2013 at 5:50 PM, Brendan Eich <brendan at mozilla.com> wrote:
Allen confirmed, but just to be clear, any world where
o["foo"]
anddo { let key = "foo"; o[key]; }
(do-expression syntax from harmony-era strawman) differ is crazytown, and we do not go there.
True, but the __proto__
train left the station bound for crazytown long
ago...
So just to be clear, the only way to add a __proto__
property to an
existing object is with Object.defineProperty
?
On Oct 15, 2013, at 3:19 PM, Dean Landolt wrote:
So just to be clear, the only way to add a
__proto__
property to an existing object is withObject.defineProperty
?
Object.mixin(obj, {["__proto__"]:42});
not if you parsed that object via JSON.parse('{"__proto__":[]}')
in this case is the equivalent of that operation through
Object.defineProperty({}, '__proto__', {enumerable: true, writable: true, configurable: true})
so that obj.__proto__
will result into property
assignment and no setter invoked.
var o = JSON.parse('{"__proto__":[]}');
o.__proto__ = Date.prototype;
o instanceof Date; // false
o.getTime === void 0; // true
delete o.__proto__; // true
// once again
o.__proto__ = Date.prototype;
// but this time ...
o instanceof Date; // true
o.getTime; // function[native]
oh, that's cute :D
too bad I cannot shim/polyfill that in my Object.mixin
module.
I would simply red-flag it and discourage the usage of __proto__
everywhere is possible (uhm wait ... I've already done that in the past,
never mind ... )
Happy __dunder__
Everybody,
Allen Wirfs-Brock wrote:
On Oct 15, 2013, at 3:19 PM, Dean Landolt wrote:
So just to be clear, the only way to add a
__proto__
property to an existing object is withObject.defineProperty
?
Object.mixin(obj, {["__proto__"]:42});
Don't forget this chestnut:
js> var o = {}
js> o.__proto__ = null
null
js> o.__proto__ = 42
42
js> o.toString
js>
yeah, if you use an alias L the footgun image comes upside down too
L.__proto__ = null;
// later on, in ES6
L.__proto__ => () pew, pew;
Let me start by apologizing for adding noise to the list.
I looked for discussion of the standardization of
__proto__
in the ES6 spec and couldn't find any. This is probably my shortcoming but I didn't know where to look or how to search the mailing list.I found a lot of threads discussing possible problems with
Object.create(null)
and__proto__
but not the discussion and decision to include it in the spec and to tell browser vendors to implement it. And then Branden's "famous" reply where he says he thinks it's a horrible idea :)Frankly, when I heard a while back it was introduced it surprised me (why would objects suddenly change their prototype?) I'm really interested in hearing the arguments and counter-arguments for/against it.
Thanks again and sorry.