Readdition of __proto__

# Benjamin (Inglor) Gruenbaum (12 years ago)

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.

# Brendan Eich (12 years ago)

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!

# Benjamin (Inglor) Gruenbaum (12 years ago)

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) :)

# Andrea Giammarchi (12 years ago)

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 swap querySelectorAll 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 not Object.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 that obj["__proto__"] or obj[key] where key is the string "__proto__" should not hot/swap the prototypal chain
  • obj.__proto__ is still valid when explicit, it should fail with Object.create(null) object but it should be a quick shortcut to create inheritance too such var obj = {__proto__: null} keeping compatibility but avoiding troubles with JSON 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

# Andrea Giammarchi (12 years ago)

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.

# Brendan Eich (12 years ago)

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 that obj["__proto__"] or obj[key] where key 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.
# Brendan Eich (12 years ago)

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:

esdiscuss/2013-October/033932

:-|.

# Andrea Giammarchi (12 years ago)

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.

# Benjamin (Inglor) Gruenbaum (12 years ago)

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.

# Brendan Eich (12 years ago)

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) but obj.__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.

# Brendan Eich (12 years ago)

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.

# Andrea Giammarchi (12 years ago)

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 of slice or push 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

# Andrea Giammarchi (12 years ago)

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

# Domenic Denicola (12 years ago)

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 :)

# Andrea Giammarchi (12 years ago)

JSON serialization <= JSON parse

# Mark S. Miller (12 years ago)

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.

# Rick Waldron (12 years ago)

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 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.

Do you mean more then inclusion in Annex B? The committee and community made the right move to go with Object.setPrototypeOf().

# Brendan Eich (12 years ago)

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!

# Brendan Eich (12 years ago)

I think you may have mixed up a few things:

  1. JSON does not recognize '__proto__' per its unchanging spec, and so parsing that identifier makes an own data property.

  2. var obj = {__proto__: proto}; is a special form, unlike any other identifier __proto__ as the literal property name does assign (set) not a define.

  3. 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 to Object.prototype. It's also delete-able (configurable), important for SES and the like.

# Andrea Giammarchi (12 years ago)

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

# Rick Waldron (12 years ago)

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.

# Allen Wirfs-Brock (12 years ago)

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 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.

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
# Andrea Giammarchi (12 years ago)

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 ?

# Allen Wirfs-Brock (12 years ago)

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.

# Andrea Giammarchi (12 years ago)

we are lucky enough there's no browser without a native JSON object that uses the D. Crockford polyfill ^_^

# Andrea Giammarchi (12 years ago)

(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.

# Dean Landolt (12 years ago)

There seems to be an interesting case missing:

let attr = "__proto__";
let o7 = new Object;
o7[attr] = p; // is this like o3?
# Benjamin (Inglor) Gruenbaum (12 years ago)

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 Wirfs-Brock (12 years ago)

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.

# Brendan Eich (12 years ago)

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.

# Dean Landolt (12 years ago)

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"] and do { 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?

# Allen Wirfs-Brock (12 years ago)

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 with Object.defineProperty?

Object.mixin(obj, {["__proto__"]:42});
# Andrea Giammarchi (12 years ago)

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]
# Andrea Giammarchi (12 years ago)

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,

# Brendan Eich (12 years ago)

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 with Object.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>
# Andrea Giammarchi (12 years ago)

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;