Standardizing __proto__

# John-David Dalton (14 years ago)

The proto property has been around for ~13yrs and is now a de facto standard supported by Chrome, Firefox, Safari, Opera, Adobe Flash, Adobe AIR, Rhino, Ringo, Narwhal, & Node.js. It's available on desktops, mobile phones, tablets, and even eReaders.

The proto property is a powerful language feature that cannot be reproduced through any existing part of the language. Current proposals like, strawman:array_create, are too limiting, (it's useful for more than just arrays) and don't cover the full functionality/flexibility of proto.

I propose making it official by beginning the process of standardizing proto or some addition of equal utility.

More info: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/proto, bugzilla.mozilla.org/show_bug.cgi?id=642500

  • John-
# Mike Shaver (14 years ago)

On Fri, Mar 18, 2011 at 9:14 AM, John-David Dalton <john.david.dalton at gmail.com> wrote:

The proto property is a powerful language feature that cannot be reproduced through any existing part of the language. Current proposals like, strawman:array_create, are too limiting, (it's useful for more than just arrays) and don't cover the full functionality/flexibility of proto.

It would be good to hear more about the use cases that aren't met by Object.create and Array.create. Mutation of prototypes of builtin objects or host objects seems unlikely to be interoperable even today.

Mike

# Oliver Hunt (14 years ago)

I believe the current hope is to kill off proto as quickly as is possible. I'm not taking a position in this argument, this is just my current understanding.

That said your examples environments that support it is slightly misleading as there's only a few JS engines being used between them: Caraken (Opera person is this correct?), JavaScriptCore, SpiderMonkey and V8. AIR uses webkit so pulls in JSC, and presumably ActionScript as well, although ActionScript is not really an ES engine so i'm unsure if it counts to this discussion.

# John-David Dalton (14 years ago)

@Oliver

That said your examples environments that support it is slightly misleading as there's only a few JS engines being used between them: Caraken (Opera person is this correct?), JavaScriptCore, SpiderMonkey and V8.   AIR uses webkit so pulls in JSC, and presumably ActionScript as well, although ActionScript is not really an ES engine so i'm unsure if it counts to this discussion.

The point is proto has a very long history and is supported by more browsers/environments than it's not.

@Mike Shaver For other possible uses please check out: msdn.microsoft.com/en-us/scriptjunkie/gg278167, jdalton/fusebox#readme and follow the bug report.

# Bradley Meck (14 years ago)

I see the use of setting prototypes at runtime, but with proxies I think it is not needed, and .proto is commonly used in attacks (webkit based browser have a couple of attacks still viable using this to get to various things). For example a prototype injection:

function foo() {};var tmp = foo.proto;foo.proto = {call:function(){return "win"}};foo.proto.proto = tmp foo.call(1)

"win"

foo(1,1)

undefined //works as intended

If a proposal could be made to prevent this sort of attack I think it could gain more traction, but first I would remove it from the original object into something like Object.setPrototype if you were to do so. Until a safe implementation of this can be determined, I doubt it will get much in terms of specification.

# Mike Shaver (14 years ago)

On Fri, Mar 18, 2011 at 9:29 AM, John-David Dalton <john.david.dalton at gmail.com> wrote:

@Mike Shaver For other possible uses please check out: msdn.microsoft.com/en-us/scriptjunkie/gg278167, jdalton/fusebox#readme

Those all look like they are needing custom-initialization, not arbitrary mutation at any point. Would you agree? For symmetry with Object.create, you might want Boolean.create, Date.create and so forth, but that's still initialization-time, and TBH I would be surprised if there were actually many collisions between libraries that augment those prototypes.

Preserving (or adding to other engines) arbitrary prototype chain mutation in order to work around name collisions seems wrong to me. Mutable proto just happened to be the hack that worked (though so did iframes), and I can't really find anything other than Fuse that uses it on the web today. The solution to name collisions is simple modules, IMO, not monkeypatching of the builtin prototype hierarchy.

I think you can also achieve what you want with Harmony proxies, so you'll have that option in the next edition of ES.

Mike

# Oliver Hunt (14 years ago)

I kind of dislike the proliferation of .create methods. It's seems inelegant. What if we had (use proto for semantic description)

Object.subclass = function(constructor) { "use strict"; function cons() { var result = constructor.apply(this, arguments); result.proto = cons.prototype; return result; } cons.prototype = Object.create(constructor.prototype); return cons; }

This would provide a function that does that allows arbitrary "subclassing" of any function. Obviously there are issues (call vs. construct behaviour, etc), but I think it's not too far off being a decent generic solution.

# John-David Dalton (14 years ago)

For symmetry with Object.create, you might want Boolean.create, Date.create and so forth, but that's still initialization-time, and TBH I would be surprised if there were actually many collisions between libraries that augment those prototypes.

In the Script Junkie article I cover a few examples of collisions with Function.prototype's and problems with Array.prototype. Date.create, Function.create and the like clutters the API. I would be for a more generic Object.setPrototypeOf() addition. es5.github.com/#x15.2.3.2

and I can't really find anything other than Fuse that uses it on the web today

PrototypeJS uses it.

I think you can also achieve what you want with Harmony proxies, so you'll have that option in the next edition of ES.

I don't know. It would need to preserve internal [[Class]] as proto.

# Allen Wirfs-Brock (14 years ago)

There is a general problem of supporting creation of special behavior objects (EG, Boolean, RegExp, anything fronted by a Proxy(??)) with arbitrary prototypes that I'm looking at for a Harmony strawman. However, I think in practice that this occurs very rarely for anything other than Array.

I would be ideal if TC39 could agree upon an Array.create proposal at its next meeting. This would enable implementations to immediately move towards providing a standards path alternative to what is probably the major use case for LHS proto.

Array create seems like a good interface for that particular use case even if the general solution that encompasses things other than Array ended up being something different.

# John-David Dalton (14 years ago)

Array create seems like a good interface for that particular use case even if the general solution that encompasses things other than Array ended up being something different.

Limiting it to Array.create seems shortsighted. It's useful for Strings, Numbers, RegExp, and others. Adding Array.create() would mean eventually adding .create() to the others (which is better than nothing), but a more generic solution would be ideal.

# Allen Wirfs-Brock (14 years ago)

The point is that there are comprehensive solutions in the works that can be part of "ES6", but those will take some time to reach consensus. If the community is looking for an immediate alternative to the most common use case of LHS proto that is not supported by ES5, then Array.create is probably something that we could decide upon very quickly. That still wouldn't preclude the more comprehensive solution.

# Allen Wirfs-Brock (14 years ago)

On Mar 18, 2011, at 10:02 AM, John-David Dalton wrote:

For symmetry with Object.create, you might want Boolean.create, Date.create and so forth, but that's still initialization-time, and TBH I would be surprised if there were actually many collisions between libraries that augment those prototypes.

In the Script Junkie article I cover a few examples of collisions with Function.prototype's and problems with Array.prototype. Date.create, Function.create and the like clutters the API. I would be for a more generic Object.setPrototypeOf() addition. es5.github.com/#x15.2.3.2

Object.setPrototypeOf is just as bad as LHS proto because is isn't atomic with object creation. That is the key thing from an implementation/optimization/security perspective. Once an object is created its [[Prototype]] needs to be immutable.

# David Bruant (14 years ago)

Le 18/03/2011 18:02, John-David Dalton a écrit :

Date.create, Function.create and the like clutters the API. I would be for a more generic Object.setPrototypeOf() addition. es5.github.com/#x15.2.3.2

The problem is that it would break an invariant of the ECMAScript language which is that the prototype of an object is set at initialization time (from script with Object.create (and soon Proxy.create), but also in internal methods) and is immutable afterward.

I think you can also achieve what you want with Harmony proxies, so you'll have that option in the next edition of ES. , ind I don't know. It would need to preserve internal [[Class]] as proto.

Proxies don't allow to set the class, indeed. Moreover, they are not optimized as arrays can be. That's the reason why Array.create and the like are a good compromise. They allow to set the prototype at object initialization and are of the right [[Class]] internally (with optimizations if there are some). But they do not break the prototype immutability invariant.

I do not think that a more generic solution is possible with all these benefits, unfortunately.

I'm glad to read that coming to an agreement on the topic is on the agenda of the next TC-39 meeting and am looking forward to see the result.

# John-David Dalton (14 years ago)

There is a general problem of supporting creation of special behavior objects

Yes, I think it would be ok to assume that the spirit of proto can be achieved by a solution that sets the [[Prototype]] at creation. This would possibly help with WebKit's security concerns while not sacrificing functionality.

# Oliver Hunt (14 years ago)

I'm still not 100% comfortable with Array.create vs. the array subtyping spec [1]. In the context of my earlier email (Object.subclass or whatever i called it) I think a subclassing function is much nicer, more generic, and doesn't require the spec to be littered with .create methods. It also extends "trivially" to non-es objects, eg. Object.subclass(Image) or somesuch in the DOM, etc.

--Oliver

[1] strawman:array_subtypes

# Boris Zbarsky (14 years ago)

On 3/18/11 1:33 PM, Oliver Hunt wrote:

It also extends "trivially" to non-es objects, eg. Object.subclass(Image) or somesuch in the DOM, etc.

Note that currently WebIDL calls for that to not work (in the sense that it examines the [[Class]] of DOM objects, so if you Object.create(new HTMLImageElement()) the thing you get won't have working DOM methods/properties on it).

Or am I misunderstanding your proposal?

# P T Withington (14 years ago)

You wouldn't need to reset the proto of result if construction and initialization were separated, if the proto of objects were only set by new constructor. If constructor.apply did not create a new object, only initialized it according to the arguments.

[The pattern you give is essentially how OpenLaszlo creates its class system, although we manually separate construction from initialization.]

Yes, I know that many constructors are already defined in the spec as creating a new object when called as a function. That's unfortunate, IMO, since in general, ES has avoided "there's more than one way".

# Oliver Hunt (14 years ago)

I think so -- my proposal doesn't take instances, nor produce instances, it takes the constructor function (Image is one of a handful of DOM constructor that can actually be used to construct things) and returns a new constructor that will produce an instances of the target object with a modified prototype chain.

# Mike Shaver (14 years ago)

On Fri, Mar 18, 2011 at 10:45 AM, Oliver Hunt <oliver at apple.com> wrote:

I think so -- my proposal doesn't take instances, nor produce instances, it takes the constructor function (Image is one of a handful of DOM constructor that can actually be used to construct things) and returns a new constructor that will produce an instances of the target object with a modified prototype chain.

Andreas in IM that we hang it on the prototype chain, so that we get it everywhere without repetitive specification. How about:

Function.prototype.createDelegatingTo = function (obj) { var o = new this; // really new-with-apply(arguments) here o.proto = obj; return o; }

Then Array.createDelegatingTo, Boolean.createDelegatingTo, Image, etc. should all work. (I like the longer, grosser name because I think it's sort of a gross pattern, and not something that should use up a concise name.)

Mike

# Allen Wirfs-Brock (14 years ago)

I think Oliver's original formulation is more correct, but regardless this really would have to be specified using ES5 pseudo code.

This is along the path of what I was calling a "generalized" solution to the problem and as such I think it needs deeper/longer consideration to be sure we have it right. I think a single argument Array.create is safer if we are looking for something that could be immediately implemented in order to start phasing out proto. Having Array.create wouldn't stop also providing a more general solution such as the one below.

# John-David Dalton (14 years ago)

I think Oliver's original formulation is more correct, but regardless this really would have to be specified using ES5 pseudo code.

This is along the path of what I was calling a "generalized" solution to the problem and as such I think it needs deeper/longer consideration to be sure we have it right. I think a single argument Array.create is safer if we are looking for something that could be immediately implemented in order to start phasing out proto. Having Array.create wouldn't stop also providing a more general solution such as the one below.

As a dev who actually uses proto as a fallback for sandboxed Arrays, I can tell you that Array.create() is not a quick, suitable solution. I support more than Array like String, RegExp, Function, Number, and Boolean. I would stress, as it seems you keep circling back to Array.create(), that Array.create() is not a fitting replacement for proto. Not even in the short term.

I can see Oliver's proposal totally fitting proto's use case, so I would +1 his Object.subclass() over Array.create(). It would be great if solutions like Oliver's Object.subclass() were discussed in the next meeting.

# Allen Wirfs-Brock (14 years ago)

Can you provide some concrete real world examples of setting alternative prototypes on Number, Boolean, and String instances? RegExp too, but that's a different beast. Also I don't believe that oliver's solution helps you much for functions.

# John-David Dalton (14 years ago)

Can you provide some concrete real world examples of  setting alternative prototypes on Number, Boolean, and String instances?  RegExp too, but that's a different beast.  Also I don't believe that oliver's solution helps you much for functions.

Yes. On my initial email I had a read more section: esdiscuss/2011-March/013131 (the fusebox repo links to screencasts on the topic too)

You may also be interested in: www.ibm.com/developerworks/web/library/wa-javascripthistory, jdalton/fusejs/blob/master/src/lang/string.js, jdalton/fusejs/blob/master/src/lang/number.js, jdalton/fusejs/blob/master/src/lang/object.js, jdalton/fusejs/blob/master/src/lang/regexp.js

# David Bruant (14 years ago)

Le 18/03/2011 18:00, Oliver Hunt a écrit :

I kind of dislike the proliferation of .create methods. It's seems inelegant. What if we had (use proto for semantic description)

Object.subclass = function(constructor) { "use strict"; function cons() { var result = constructor.apply(this, arguments); result.proto = cons.prototype; return result; } cons.prototype = Object.create(constructor.prototype); return cons; }

Mike Shaver:

Andreas in IM that we hang it on the prototype chain, so that we get it everywhere without repetitive specification. How about:

Function.prototype.createDelegatingTo = function (obj) { var o = new this; // really new-with-apply(arguments) here o.proto = obj; return o; }

One difference I see between Object.subclass and Function.prototype.createDelegatingTo is that the latter does not enforce anything on the prototype.

( Array.createDelegatingTo({}) ) instanceof Array === false

// while

( new Object.subclass(Array) ) instanceof Array === true

On the other hand, Function.prototype.createDelegatingTo is more expressive and Object.subclass can be implemented from it. If methods such as Array.create are to be considered, this issue of enforcing Array.prototype will have to be addressed. I have personnally no strong conviction on one side or the other.

# Brendan Eich (14 years ago)

On Mar 18, 2011, at 10:02 AM, John-David Dalton wrote:

For symmetry with Object.create, you might want Boolean.create, Date.create and so forth, but that's still initialization-time, and TBH I would be surprised if there were actually many collisions between libraries that augment those prototypes.

In the Script Junkie article I cover a few examples of collisions with Function.prototype's and problems with Array.prototype. Date.create, Function.create and the like clutters the API. I would be for a more generic Object.setPrototypeOf() addition.

You are missing shaver's point that initiializing is not the same as reassigning later on a created and reachable object.

We really do not want to support reassignment in our implementation, and I suspect other implementors feel the same. It's a sunk cost, but see the sunk-cost fallacy. Nothing is free, people still pay with security attack surface and consequent (both friendly fuzzers and bad actors attack that surface) bug fixing.

es5.github.com/#x15.2.3.2

and I can't really find anything other than Fuse that uses it on the web today

PrototypeJS uses it.

Prototype gets proto but AFAICS does not set it.

I think you can also achieve what you want with Harmony proxies, so you'll have that option in the next edition of ES.

I don't know. It would need to preserve internal [[Class]] as proto.

Proxies may help some use-cases. They are not a solution if you want a one-off delegation chain from a non-proxy to another object.

# Brendan Eich (14 years ago)

On Mar 18, 2011, at 9:20 AM, Oliver Hunt wrote:

I believe the current hope is to kill off proto as quickly as is possible. I'm not taking a position in this argument, this is just my current understanding.

This makes it sound quick. There's no way to guarantee that.

Also, to kill it we need replacements for the valid use-cases, assuming we agree on those. These replacements need to have the right "fit" as well as "form", i.e., they must be usable.

Here's my framework for thinking about deprecation and obsolescence of proto:

  1. A "get" equivalent: Object.getPrototypeOf in ES5.

  2. A "define" (initialize on a newborn and otherwise inaccessible object) equivalent: see Object.create, but people criticize it on usability grounds. So, see Allen's mail and the strawman he is championing:

strawman:obj_initialiser_meta

  1. Ship in multiple browsers, so library authors can detect and start to use them.

  2. Wait a decent interval to evaluate usability.

  3. Announce removal of proto in subsequent releases of those multiple browsers.

  4. Evaluate market share of downrev browsers as those release dates approach.

  5. If all looks good, remove proto, done.

  6. Else goto one of the earlier steps, possibly adjusting specs and impls based on feedback and uptake.

This is not going to be "quick".

# John-David Dalton (14 years ago)

You are missing shaver's point that initiializing is not the same as reassigning later on a created and reachable object.

No, I got it. I can dig something like Object.subclass() or the other alternatives proposed that handle setting the [[Prototype]] one time at creation.

Proxies may help some use-cases. They are not a solution if you want a one-off delegation chain from a non-proxy to another object.

Ya proxies are a miss for this. It's been stated that proxies will not persist the [[Class]] so they aren't an ideal replacement for proto.

# Brendan Eich (14 years ago)

On Mar 18, 2011, at 1:08 PM, John-David Dalton wrote:

You are missing shaver's point that initiializing is not the same as reassigning later on a created and reachable object.

No, I got it.

Sorry I didn't read ahead -- everyone seems to get this point now, which is progress.

Did I miss new, mutating uses of proto in PrototypeJS?

# John-David Dalton (14 years ago)

Did I miss new, mutating uses of proto in PrototypeJS?

Nope, nothing new in PrototypeJS. Mutating use of proto can be found in jdalton/fusejs as 1 of the 3 techniques used. The proto fork is only being used by Safari (because of a loong time iframe bug bugs.webkit.org/show_bug.cgi?id=25079) as well as possible server side usage.

I think it's good to start looking into alternatives to proto and I would love an official standardized alternative :D Also very happy you wrote that this will not be a quick change.

# Allen Wirfs-Brock (14 years ago)

On Mar 18, 2011, at 12:33 PM, John-David Dalton wrote:

Can you provide some concrete real world examples of setting alternative prototypes on Number, Boolean, and String instances? RegExp too, but that's a different beast. Also I don't believe that oliver's solution helps you much for functions.

Yes. On my initial email I had a read more section: esdiscuss/2011-March/013131 (the fusebox repo links to screencasts on the topic too)

You may also be interested in: www.ibm.com/developerworks/web/library/wa-javascripthistory, jdalton/fusejs/blob/master/src/lang/string.js, jdalton/fusejs/blob/master/src/lang/number.js, jdalton/fusejs/blob/master/src/lang/object.js, jdalton/fusejs/blob/master/src/lang/regexp.js

I think I understand what you doing here, especially for Array but I don't think it really addresses my Number, Boolean, String question. Instances of those instances usually aren't created via explicit constructor calls but by implicit conversions that the ES5 spec. "hardwires" to the appropriate built-in prototype object. In the most common cases for es5, say "string".concat(foo) a string object isn't even created for the property lookup.

I'm skeptical that many developers actually would code: fs.String("stirng").concat(foo) rather than"string".concat(foo). How much actual usage do you see of the Number, Boolean, and String cases?

# Dmitry A. Soshnikov (14 years ago)

JFTR, I remember the previous the same discussion(s) and mentioned earlier the thing similar to this Object.subclass, though, just with providing an ability to specify the [[Class]] for Object.create. E.g. for a unclassified inheritance:

let foo = Object.create(Array.prototype, "Array", {...});

and for classified as well:

// create new constructor, objects of which // inherit from Array.prototype and are real // arrays, additionally pass prototype properties var Foo = Constructor.create({ // objects kind class: "Array", // an initializer constructor: function Foo(args) { this.push.apply(this, args); }, // prototype properties (also may // be added after "Foo" is created prototype: { size: { get: function getSize() { return this.length; } } } }); var foo = new Foo([1, 2, 3]); console.log(foo.length); // 3 foo.push(4, 5, 6); console.log(foo); // 1,2,3,4,5,6 console.log(foo.length); // 6 foo[7] = 8; console.log(foo); // 1,2,3,4,5,6,,8 foo.length = 3; console.log(foo); // 1,2,3

etc.

Other examples: DmitrySoshnikov/es-laboratory/blob/master/examples/create.js

P.S.: yeah, mutable proto is (was?) and interesting feature, but we tried to find its real practical wide-spread application and couldn't. The same as we couldn't find the big practical rationale of the Python's class from which proto is borrowed. Yes, repeat, it's interesting from the completely dynamic languages ideology (I like to show examples of first-class dynamic classes as oppose to second-class static classes), but if to start just analyze and try get again

# John-David Dalton (14 years ago)

I think I understand what you doing here, especially for Array but I don't think it really addresses my  Number, Boolean, String question. Instances of those instances usually aren't created via explicit constructor calls but by implicit conversions that the ES5 spec. "hardwires" to the appropriate built-in prototype object. In the most common cases for es5, say "string".concat(foo) a string object isn't even created for the property lookup.

I'm skeptical that many developers actually   would code: fs.String("stirng").concat(foo) rather than"string".concat(foo).  How much actual usage do you see of the Number, Boolean, and String cases?

Ya, some people have that reaction at first, but after use it's not bad. Most of the time you create a string or value once then pass around the variable. Because these sandboxed natives chain, usage is natural. As such fuse.String(...).split(' ') produces a sandboxed Array so you go from 1 sandboxed native to another without hassle. Same goes for fuse.Array(1,2,3).join() -> a sandboxed String value.

Generics are also supported fuse.String.concat(str, other) so you can pass in regular natives too.

PrototypeJS and other libs use wrapper/utilities for strings, DOM elements, arrays, ... so using fuse.String() is just as easy as say $w(). By using the framework it becomes second nature to assume, for example, fuse(...).getAttribute(..) returns a sandboxed String value.

# David Bruant (14 years ago)

Le 18/03/2011 18:00, Oliver Hunt a écrit :

I kind of dislike the proliferation of .create methods. It's seems inelegant. What if we had (use proto for semantic description)

Object.subclass = function(constructor) { "use strict"; function cons() { var result = constructor.apply(this, arguments); result.proto = cons.prototype; return result; } cons.prototype = Object.create(constructor.prototype); return cons; }

This would provide a function that does that allows arbitrary "subclassing" of any function. Obviously there are issues (call vs. construct behaviour, etc), but I think it's not too far off being a decent generic solution.

After giving it further thought, I think that this is a good idea, but the syntax is wrong. You'll probably agree that the constructor argument needs a [[Constructor]] internal method, but your function would need to filter out things given as arguments which don't. To better target "things that have a [[Constructor]] internal method", I would add something to Function.prototype (as it is done with the Function.prototype.createDelegatingTo idea). So I would go more for something like:

Function.prototype.subclass = function() { function cons() { var result = new this; // throws if this doesn't have an internal [[Construct]] result.proto = cons.prototype; return result; } cons.prototype = Object.create(this.prototype); return cons.bind(this); }

This also addresses the issue you raise "(call vs. construct behaviour)".

# Mike Shaver (14 years ago)

On Fri, Mar 18, 2011 at 1:53 PM, John-David Dalton <john.david.dalton at gmail.com> wrote:

Ya, some people have that reaction at first, but after use it's not bad. Most of the time you create a string or value once then pass around the variable. Because these sandboxed natives chain, usage is natural. As such fuse.String(...).split(' ') produces a sandboxed Array so you go from 1 sandboxed native to another without hassle. Same goes for fuse.Array(1,2,3).join() -> a sandboxed String value.

Generics are also supported fuse.String.concat(str, other) so you can pass in regular natives too.

PrototypeJS and other libs use wrapper/utilities for strings, DOM elements, arrays, ... so using fuse.String() is just as easy as say $w(). By using the framework it becomes second nature to assume, for example, fuse(...).getAttribute(..) returns a sandboxed String value.

But AIUI the point of sandboxing is to keep different libraries from stepping on each other's toes, and AFAICT such libraries use literals (string, array, object and regexp) quite liberally. In Prototype all 4 are used. In jQuery all 4 are used and passing in a String object where a string primitive is expected will fail because of explicit typeof checks.

If you do control the code in question, you can just write your code to use a namespace (Object.prototype.ns.method(), Object.prototype.ns2.method()) as easily as sprinkling |fuse.| everywhere, and without giving up the ability to use literals or closures (!).

Mike

# John-David Dalton (14 years ago)

But AIUI the point of sandboxing is to keep different libraries from stepping on each other's toes, and AFAICT such libraries use literals (string, array, object and regexp) quite liberally.

Sandbox natives are used so FuseJS plays well with others while allowing the extension/modification of it's own set of natives. It's not about fix existing lib compatibility.

In Prototype all 4 are used.

But they also extended global natives which causes conflicts. For example ExtJS adds Function#delay but so does PrototypeJS, and when used together they cause a JS errors. OR when MooTools/Prototype added Array#toJSON which broke native JSON for over a year and caused conflicts with YUI.

In jQuery all 4 are used and passing in a String object where a string primitive is expected will fail because of explicit typeof checks.

Again not for jQuery. They have their own hurdles with an overly generic jQuery function, where attempting to check for isPlainObject is necessarily.

If you do control the code in question, you can just write your code to use a namespace (Object.prototype.ns.method(),

Extending the Object.prototype is a compatibility nightmare and extending natives causes third party script headaches. Sandboxed natives behave like natives and have the correct internal [[Class]] values so you can extend/customize without polluting the ones on the global.

You can even create more than one of say the Array constructor as with fuse.Array and fuse.dom.NodeList (also an Array constructor but with DOM methods on it's prototype)

And this is possible, in part, because of proto.

# Mike Shaver (14 years ago)

On Fri, Mar 18, 2011 at 5:11 PM, John-David Dalton <john.david.dalton at gmail.com> wrote:

Extending the Object.prototype is a compatibility nightmare

It was a compatibility nightmare when people didn't namespace, and when you couldn't make non-enumerable properties. Using a namespace for additions to the prototype chain and using ES5's defineProperty to keep those additions from affecting enumeration behaviour seems like it overcomes those, at least to me.

Mike

# Brendan Eich (14 years ago)

On Mar 18, 2011, at 1:46 PM, Dmitry A. Soshnikov wrote:

Other examples: DmitrySoshnikov/es-laboratory/blob/master/examples/create.js

Thanks, this is helpful.

So the mutable proto (no matter in which name and view) can be standardized only if (1) no security issues, (2.1) practically needed or (2.2) just "a cool stuff of dynamic languages" with unfortunately small practical application.

(1) is more about quality of implementation, extensions vs. fuzz-re-testing, and general cost of mutable proto. The verdict in my experience is negative.

(2.1) is false on its face: we can provide narrower special forms to cover the "define" (set on a newborn or otherwise-inaccessible object) use-case.

(2.2) is not just false, but permanently so: "no" on its face. You can't justify an intrusive feature with buzzword momentum.

To avoid i-looping, Object.setPrototype(object, parent); is better than proto of course.

Assignable proto requires cycle checking with an exception thrown on detected cycle, of course. Implementations already do this check, and I do not see how Object.setProottypeOf avoids the need for it.

In terms of standardization, Object.setPrototypeOf is a non-starter.

# Allen Wirfs-Brock (14 years ago)

The private names proposal would private another way to avoid creating extension methods with conflicting names strawman:private_names

# John-David Dalton (14 years ago)

It was a compatibility nightmare when people didn't namespace, and when you couldn't make non-enumerable properties. Using a namespace for additions to the prototype chain and using ES5's defineProperty to keep those additions from affecting enumeration behaviour seems like it overcomes those, at least to me.

Unfortunately not all browsers/environments support ES5 defineProperty, and not all third party code, widgets, ads, and js libs guard against extensions in their for-in loops.

# Brendan Eich (14 years ago)

On Mar 18, 2011, at 5:11 PM, John-David Dalton wrote:

You can even create more than one of say the Array constructor as with fuse.Array and fuse.dom.NodeList (also an Array constructor but with DOM methods on it's prototype)

And this is possible, in part, because of proto.

But to get back to shaver's point: you are requiring a stylized, non-standard, popular-library-incompatible dialect of JS to be used from the start to work inside FuseJS's sandboxes, IIUC. Right?

That's ok, not criticizing per se. Just noting it's yet another dialect. There are many such, some with translators that can handle the wrapping, even for primitives and literals, for you.

Harmony is not about standardizing whatever hacks you needed to make your dialect work. It's about core language features that complete and correct (by extension if not incompatible change) the incomplete and sometimes messy core language, even as of ES5.

# John-David Dalton (14 years ago)

But to get back to shaver's point: you are requiring a stylized, non-standard, popular-library-incompatible dialect of JS to be used from the start to work inside FuseJS's sandboxes, IIUC. Right?

Mike was getting OT onto the specifics and merits of sandboxed natives/FuseJS. I was originally answering Allen's prompt for examples of other non Array uses for proto.

That's ok, not criticizing per se. Just noting it's yet another dialect. There are many such, some with translators that can handle the wrapping, even for primitives and literals, for you.

Non that provide the flexibility of extending prototypes of natives directly without the risk of conflict and none that work in browsers/environments from Safari 2.0.0 through IE9.

Harmony is not about standardizing whatever hacks you needed to make your dialect work.

Thanks for the hacks comment.

It's about core language features that complete and correct (by extension if not incompatible change) the incomplete and sometimes messy core language, even as of ES5.

I and others find proto useful and gave a good use-case as prompted. I am ok with proto's eventual removal but as you stated earlier there should be a replacement for use-cases. The proto property is a de facto standard and should be standardized in some form to ensure compatibility, consistency, and security.

As mentioned before Array.create() is not enough, something like Object.subclass() as previously proposed would be flexible for most use-cases and eliminate some of the perf/security concerns implementers have.

# Brendan Eich (14 years ago)

On Mar 18, 2011, at 7:27 PM, John-David Dalton wrote:

But to get back to shaver's point: you are requiring a stylized, non-standard, popular-library-incompatible dialect of JS to be used from the start to work inside FuseJS's sandboxes, IIUC. Right?

Mike was getting OT onto the specifics and merits of sandboxed natives/FuseJS.

It's not OT to try to quantify, even roughly, how often proto mutation matters (in static lines of code if not lines run at runtime per unit time). If proto mutation is really rare compared to reading, and especially so if you exclude the "define" or "presetting not resetting" issue, then we should not linger on this too long.

We still need to serve the preset-proto use cases, for sure.

# Kyle Simpson (14 years ago)

There's LOTS of sites out there that still (unfortunately) do unsafe overwriting/overloading of the native's prototypes. For instance, just a few months ago, I ran across a site that was creating a Array.prototype.push() implementation that was incompatible with the standard implementation. When I injected jQuery onto that page, jQuery failed to work because Sizzle relies on being able to call push() with multiple parameters (something the page's .push() didn't handle). And there are many, many other examples, like adding String.prototype.trim(), etc.

The point? If everyone were in the habit of using sandboxable natives, like FuseBox provides, then that page could override it's version of Array all it wanted (even the native one), and my code, using Fuse.Array, would be perfectly safe.

Sandboxing a native-like object is just as much about preventing my changes from affecting others as it is about protecting myself from what others do.

Now, can I achieve the same thing without sandboxed natives? Of course. I can make fake data structure wrappers for every data type I care about. But I lose a lot of the semantics, operators, syntax-sugar of the actual natives. For instance, it's REALLY nice that a sandbox'd Array still lets me use the [] operator to access indices, etc. Is it perfect? No. But it's a LOT better than just choosing some custom namespace for my app and creating all new data structure wrappers. And in many cases, it's more efficient/performant, too.

To reiterate what John said earlier: The spirit of what FuseBox does doesn't require the mutability of the proto, but since at the moment there is no way to set the [[Prototype]]/[[Class]] of an object at creation time, proto is the only option in some browsers (where iframe is buggy). If we can agree on something that allows such behavior at creation of an object, including Function objects (because I personally use a variation of FuseBox techniques to sandbox my functions), then proto becomes unnecessary.

# John-David Dalton (14 years ago)

We still need to serve the preset-proto use cases, for sure.

I think that's what I am championing for (something that allows setting [[Prototype]] on creation like Object.subclass() )? If so then we are on the same page \o/

# Erik Corry (14 years ago)

I don't expect big performance gains from making proto non-overwritable. In non-optimized code you have to check the prototype chain in case the prototype objects (not their identity) have been changed. In the case of optimized code you can fall back to nonoptimized code if someone writes to proto. So it wouldn't make all that much difference, at least in V8-Crankshaft.

On the other hand I am very leery of breaking pages that used to work. Of course it is a blessing in this case that IE never adopted this particular misfeature of the language, but our experience with making backwards incompatible changes is that you can't do it without breaking part of the web. It's IMHO an illusion to think that you can enumerate all uses of a feature before you break it, and I think it is even an illusion to think that everyone will complain to you after you break it. They (developers and users) will just sigh and think to themselves that the web is a substandard platform on which things that used to work suddenly stop working. I can't see us leading the way on this (no surprise there).

2011/3/19 John-David Dalton <john.david.dalton at gmail.com>:

# Garrett Smith (14 years ago)

On 3/18/11, Kyle Simpson <getify at gmail.com> wrote:

There's LOTS of sites out there that still (unfortunately) do unsafe overwriting/overloading of the native's prototypes. For instance, just a few months ago, I ran across a site that was creating a Array.prototype.push() implementation that was incompatible with the standard implementation. When I injected jQuery onto that page, jQuery failed to work because Sizzle relies on being able to call push() with multiple parameters (something the page's .push() didn't handle). And there are many, many other examples, like adding String.prototype.trim(), etc.

Yep. And JSON.parse, Function.prototype.apply, and others. Modifying built-ins' prototypes is a powerful feature and modifying proto is even more powerful.

The point? If everyone were in the habit of using sandboxable natives, like FuseBox provides, then that page could override it's version of Array all it wanted (even the native one), and my code, using Fuse.Array, would be perfectly safe.

The example you mention creates a problem that can be avoided by instead using a feature test. It needs at least:

if(!Array.prototype.push) {

}

Just doing that would avoid the problem, thus obviating the need for a workaround (with Sanboxes, etc).

I'm not arguing that it is wrong to standardize proto, just that your example doesn't seem to support doing that.

# Geoffrey Sneddon (14 years ago)

On 18/03/11 20:05, Brendan Eich wrote:

On Mar 18, 2011, at 9:20 AM, Oliver Hunt wrote:

I believe the current hope is to kill off proto as quickly as is possible. I'm not taking a position in this argument, this is just my current understanding.

This makes it sound quick. There's no way to guarantee that.

Also, to kill it we need replacements for the valid use-cases, assuming we agree on those. These replacements need to have the right "fit" as well as "form", i.e., they must be usable.

Here's my framework for thinking about deprecation and obsolescence of proto:

  1. A "get" equivalent: Object.getPrototypeOf in ES5.

  2. A "define" (initialize on a newborn and otherwise inaccessible object) equivalent: see Object.create, but people criticize it on usability grounds. So, see Allen's mail and the strawman he is championing:

strawman:obj_initialiser_meta

  1. Ship in multiple browsers, so library authors can detect and start to use them.

  2. Wait a decent interval to evaluate usability.

  3. Announce removal of proto in subsequent releases of those multiple browsers.

  4. Evaluate market share of downrev browsers as those release dates approach.

  5. If all looks good, remove proto, done.

  6. Else goto one of the earlier steps, possibly adjusting specs and impls based on feedback and uptake.

This is not going to be "quick".

Is there much to be gained by removing proto entirely? IIRC the original proposal was just to change it to being [[Writeable]]: false. Sure, still having it around isn't as nice as removing it entirely, but the cost of removing it would appear to be fairly high (it's fairly widely used in a read-only sense, and getting all that content to change is a lot of work for IMO not that much gain).

# Brendan Eich (14 years ago)

On Apr 21, 2011, at 6:11 AM, Geoffrey Sneddon wrote:

On 18/03/11 20:05, Brendan Eich wrote:

... 8. Else goto one of the earlier steps, possibly adjusting specs and impls based on feedback and uptake.

This is not going to be "quick".

Is there much to be gained by removing proto entirely? IIRC the original proposal was just to change it to being [[Writeable]]: false. Sure, still having it around isn't as nice as removing it entirely, but the cost of removing it would appear to be fairly high (it's fairly widely used in a read-only sense, and getting all that content to change is a lot of work for IMO not that much gain).

Obviously I agree that proto is hard to kill. Hence my reply to Oliver's "as quickly as possible", which does not say how quickly, but which suggests with all due haste (where our haste does not mean a thing to absent or unmotivated content developers).

OTOH we don't need to standardize proto. We might instead poison-pill it in Harmony, so opting in involves an early error on every use of proto, and you have to migrate by switching to Object.getPrototypeOf or an object initialiser extension that allows presetting the new object's prototype chain link.

I don't recommend we do this. Poison pills for arguments.callee, etc., were one thing (and arguments.callee in particular is a reflective API not replaceable with named function expressions, since it can be eval'ed into arbitrary function code where the enclosing function may not have a single known name). ES5 strict was trying to get rid of capability leaks ('callee'), and stack walking hazards ('caller').

With non-writable proto and Object.getPrototypeOf, there's no capability leak. The two are (or should be, modulo implementation baggage) equivalent.

Plus, a poison pill is yet another migration tax. Even for the good of eliminating proto some decades hence, it is potentially high: some developers may hit the early error and decide "screw Harmony!" and stick with the default script type. This is a non-trivial risk of poison-pilling proto, and it goes directly against the proposed good of eliminating proto usage in the wild.

My argument leaves us with proto as non-standard extension, to wean people off of if we can, via docs and evangelism. This seems a better course to me than premature (anti-)standardization.

# David Herman (14 years ago)

OTOH we don't need to standardize proto. We might instead poison-pill it in Harmony, so opting in involves an early error on every use of proto, and you have to migrate by switching to Object.getPrototypeOf or an object initialiser extension that allows presetting the new object's prototype chain link.

I know you aren't recommending this, but I just want to add more reasons why it's a bad idea. It would preserve the specialness of "proto" when the hope is that we could eventually get to a point where it's not special anymore; that's the point of Object.getPrototypeOf being a static method. Forcing every object to have special magic properties breaks abstractions and breaks use cases like object-as-table.

And an early error just means people would hack around it by using obj["proto"] or obj[(function(){return "proto"})()] or whatever they needed to circumvent the static analysis -- assuming we don't solve the halting problem in the meantime, of course. ;)

My argument leaves us with proto as non-standard extension, to wean people off of if we can, via docs and evangelism. This seems a better course to me than premature (anti-)standardization.

Agreed, 100%.