Standard builtins' prototypes and toString

# Till Schneidereit (11 years ago)

While working on changing Date.prototype to be a plain object in SpiderMonkey, we realized that there's an issue: the way things are specced now, alert(Date.prototype) will throw, because Date.prototype.toString isn't generic. The same applies for all builtins with non-generic toString prototype functions.

To resolve this, I propose changing these toString to first check if the this value is %FooPrototype% (e.g., %DatePrototype% in the case at hand) and return the result of the equivalent of calling the original Object.prototype.toString.

I'm not sure if that is enough to cover subclasses of these builtins. Will calling toString on the prototype of class MyDate extends Date{} still throw? If so, that would at least not be a backwards-compatibility concern, but it's probably also not desirable.

till

# Mark S. Miller (11 years ago)

Wow, what a mess. Let's forget the builtins for a moment and focus on JS classes, both the old/current patterns for coding these manually, and the new ES6 class syntax. Consider:

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  },
  getX() { return this.x; },
  getY() { return this.y; },
  toString() {
    return `<${this.getX()},${this.getY()}>`;
  }
}

or equivalently enough in ES5

function Point(x, y) {
  this.x = x;
  this.y = y;
}
Point.prototype = {
  getX: function() { return this.x; },
  getY: function() { return this.y; },
  toString: function() {
    return '<' + this.getX() + ',' + this.getY() + '>';
  }
};

alert(Point.prototype) alerts <undefined,undefined>. Ok, this specific example doesn't throw, but equally simple and plausible examples would.

# Jafar Husain (11 years ago)

It was pointed out to me that I forgot to include the latest slides for async generator. I was referring to the link included earlier in the thread. Sorry for the confusion.

Here is the link again.

docs.google.com/a/netflix.com/file/d/0B4PVbLpUIdzoMDR5dWstRllXblU

Dictated using voice recognition. Please forgive the typos.

# Allen Wirfs-Brock (11 years ago)

On Jun 12, 2014, at 5:26 AM, Till Schneidereit wrote:

While working on changing Date.prototype to be a plain object in SpiderMonkey, we realized that there's an issue: the way things are specced now, alert(Date.prototype) will throw, because Date.prototype.toString isn't generic. The same applies for all builtins with non-generic toString prototype functions.

Fortunately there aren't very many of those. I think it is only Date and RegExp that have this issue among the ES6 built-ins

To resolve this, I propose changing these toString to first check if the this value is %FooPrototype% (e.g., %DatePrototype% in the case at hand) and return the result of the equivalent of calling the original Object.prototype.toString.

that breaks if you move such methods across Realms.

I'm not sure if that is enough to cover subclasses of these builtins. Will calling toString on the prototype of class MyDate extends Date{} still throw? If so, that would at least not be a backwards-compatibility concern, but it's probably also not desirable.

Yes, it would still throw for subclasses.

I think the pattern we should follow for such built-in toString methods is that if a branding check of this sort is performed, the fall back should be to perform the built-in Object.prototype.toString behavior rather than throwing.

Unless somebody sees issues with this fix, I'll incorporate it into the spec. for Date and RegExp.

Of course, even with this fix there is no guarantee that invoking toString on an object won't throw. Debugging tool that expect to use toString need to take that into account and provide their own fallbacks.

# Till Schneidereit (11 years ago)

On Thu, Jun 12, 2014 at 5:19 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>

wrote:

On Jun 12, 2014, at 5:26 AM, Till Schneidereit wrote:

While working on changing Date.prototype to be a plain object in SpiderMonkey, we realized that there's an issue: the way things are specced now, alert(Date.prototype) will throw, because Date.prototype.toString isn't generic. The same applies for all builtins with non-generic toString prototype functions.

Fortunately there aren't very many of those. I think it is only Date and RegExp that have this issue among the ES6 built-ins

To resolve this, I propose changing these toString to first check if the this value is %FooPrototype% (e.g., %DatePrototype% in the case at hand) and return the result of the equivalent of calling the original Object.prototype.toString.

that breaks if you move such methods across Realms.

Good point.

I'm not sure if that is enough to cover subclasses of these builtins. Will calling toString on the prototype of class MyDate extends Date{} still throw? If so, that would at least not be a backwards-compatibility concern, but it's probably also not desirable.

Yes, it would still throw for subclasses.

I think the pattern we should follow for such built-in toString methods is that if a branding check of this sort is performed, the fall back should be to perform the built-in Object.prototype.toString behavior rather than throwing.

That's what I was trying to say above, yes. The exact means of the branding are less important, I think.

Unless somebody sees issues with this fix, I'll incorporate it into the spec. for Date and RegExp.

Of course, even with this fix there is no guarantee that invoking toString on an object won't throw. Debugging tool that expect to use toString need to take that into account and provide their own fallbacks.

Sure. This has always been the case and is, I guess, just a consequence of how methods work in JS.

# Mark Miller (11 years ago)

On Thu, Jun 12, 2014 at 8:19 AM, Allen Wirfs-Brock <allen at wirfs-brock.com>

wrote:

On Jun 12, 2014, at 5:26 AM, Till Schneidereit wrote:

While working on changing Date.prototype to be a plain object in SpiderMonkey, we realized that there's an issue: the way things are specced now, alert(Date.prototype) will throw, because Date.prototype.toString isn't generic. The same applies for all builtins with non-generic toString prototype functions.

Fortunately there aren't very many of those. I think it is only Date and RegExp that have this issue among the ES6 built-ins

WeakMap, Map, Set, others?

To resolve this, I propose changing these toString to first check if the this value is %FooPrototype% (e.g., %DatePrototype% in the case at hand) and return the result of the equivalent of calling the original Object.prototype.toString.

that breaks if you move such methods across Realms.

I'm not sure if that is enough to cover subclasses of these builtins. Will calling toString on the prototype of class MyDate extends Date{} still throw? If so, that would at least not be a backwards-compatibility concern, but it's probably also not desirable.

Yes, it would still throw for subclasses.

I think the pattern we should follow for such built-in toString methods is that if a branding check of this sort is performed, the fall back should be to perform the built-in Object.prototype.toString behavior rather than throwing.

Unless somebody sees issues with this fix, I'll incorporate it into the spec. for Date and RegExp.

The real problem includes ES6 classes as well. Whatever fix we choose, it should apply there as well -- not that I have a concrete proposal. This one's a real puzzler.

# C. Scott Ananian (11 years ago)

It would be slightly more "JavaScripty" to have Date.prototype.[[DateValue]] exist, and be set to the epoch or some such.

This problem actually seems to be an artifact of the way that [[Construct]] works in ES6 -- but the takeaway is that prototypes of a class are not themselves instances of the class. It's not surprising that methods of the class thus don't work on the prototype. I'd vote WONTFIX.

# Allen Wirfs-Brock (11 years ago)

On Jun 12, 2014, at 8:24 AM, Mark Miller wrote:

On Thu, Jun 12, 2014 at 8:19 AM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

Fortunately there aren't very many of those. I think it is only Date and RegExp that have this issue among the ES6 built-ins

WeakMap, Map, Set, others?

Nope, they don't even have custom toString methods.

The real problem includes ES6 classes as well. Whatever fix we choose, it should apply there as well -- not that I have a concrete proposal. This one's a real puzzler.

By default they just inherit up the prototype chain, typically to Object.prototype.toSring

If a JS programmer chooses to over-ride toString, then it becomes their problem.

The same best practice should be taught to them too, if you are going to do a branding check in a toString method you should fall back to the default object behavior:

class MyClass {
   toString() {
      if (!myBrand(this)) return super.toString();
      ...
    }
}

We can't do this for them because there is no universal branding concept we can apply for them.

# Allen Wirfs-Brock (11 years ago)

On Jun 12, 2014, at 8:28 AM, C. Scott Ananian wrote:

It would be slightly more "JavaScripty" to have Date.prototype.[[DateValue]] exist, and be set to the epoch or some such.

This problem actually seems to be an artifact of the way that [[Construct]] works in ES6 -- but the takeaway is that prototypes of a class are not themselves instances of the class. It's not surprising that methods of the class thus don't work on the prototype. I'd vote WONTFIX. --scott

TC39 explicitly agreed that we would move away from the "a prototype is an instance of its constructor" model, except where there was known legacy usage that we had to support. That previous model make it very difficult to generalize the initialization of prototype objects created via class declarations.

# André Bargull (11 years ago)

On Jun 12, 2014, at 5:26 AM, Till Schneidereit wrote:

/ While working on changing Date.prototype to be a plain object in SpiderMonkey, we realized that there's an issue: the way things are specced now, alert(Date.prototype) will throw, because Date.prototype.toString isn't generic. The same applies for all builtins with non-generic toString prototype functions. / Fortunately there aren't very many of those. I think it is only Date and RegExp that have this issue among the ES6 built-ins

And Number.prototype, String.prototype, Boolean.prototype and Symbol.prototype. And actually it's even worse for Symbol.prototype because of the @@toPrimitive override.

  • André
# Boris Zbarsky (11 years ago)

On 6/12/14, 11:45 AM, André Bargull wrote:

And Number.prototype, String.prototype, Boolean.prototype

All of those have the relevant internal fields, so they don't have the problem.

e.g. Number.prototype.toString() returns 0.

and Symbol.prototype.

This one has the issue, though.

# C. Scott Ananian (11 years ago)

On Thu, Jun 12, 2014 at 11:42 AM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

TC39 explicitly agreed that we would move away from the "a prototype is an instance of its constructor" model, except where there was known legacy usage that we had to support. That previous model make it very difficult to generalize the initialization of prototype objects created via class declarations.

Right, which is why I'm saying I don't understand the problem with Date.prototype.toString(). It's not a Date, it's a Date.prototype. Throwing an error is more informative than hiding it and returning something arbitrary. As has been mentioned, debuggers have to deal with the fact that toString can have side-effects, throw exceptions, etc, anyway. What problem are we actually solving here?

# Allen Wirfs-Brock (11 years ago)

On Jun 12, 2014, at 8:45 AM, André Bargull wrote:

On Jun 12, 2014, at 5:26 AM, Till Schneidereit wrote:

While working on changing Date.prototype to be a plain object in SpiderMonkey, we realized that there's an issue: the way things are specced now, alert(Date.prototype) will throw, because Date.prototype.toString isn't generic. The same applies for all builtins with non-generic toString prototype functions.

Fortunately there aren't very many of those. I think it is only Date and RegExp that have this issue among the ES6 built-ins

And Number.prototype, String.prototype, Boolean.prototype and Symbol.prototype. And actually it's even worse for Symbol.prototype because of the @@toPrimitive override.

Right, I left those out because I thought we didn't have an issue with them, but you're right. I'll fix them too.

# Erik Arvidsson (11 years ago)

On Thu Jun 12 2014 at 11:28:12 AM, C. Scott Ananian <ecmascript at cscott.net>

wrote:

It would be slightly more "JavaScripty" to have Date.prototype.[[DateValue]] exist, and be set to the epoch or some such.

+1

  1. Let date be the this value.
  2. If Type(date) is not Object then, throw a TypeError exception.
  3. If date does not have a [[DateValue]] internal slot, then let tv be NaN
  4. Else let tv be this time value.
  5. Return ToDateString(tv).

Allen, what is the benefit to do super.toString() instead?

# Till Schneidereit (11 years ago)

On Thu, Jun 12, 2014 at 5:55 PM, Boris Zbarsky <bzbarsky at mit.edu> wrote:

On 6/12/14, 11:45 AM, André Bargull wrote:

And Number.prototype, String.prototype, Boolean.prototype

All of those have the relevant internal fields, so they don't have the problem.

e.g. Number.prototype.toString() returns "0".

They won't have those fields much longer if the changes in annex E.1[1] work out.

[1]: Search for "Date" in people.mozilla.org/~jorendorff/es6-draft.html#sec

# C. Scott Ananian (11 years ago)

FWIW, in Chrome 36:

> Date.prototype.toString()
"Invalid Date"
> ({ toString: Date.prototype.toString }).toString()

TypeError: this is not a Date object.
> Number.prototype.toString()
"0"
> String.prototype.toString()
""
> Boolean.prototype.toString()
"false"
> RegExp.prototype.toString()
"/(?:)/"

That last one is a bit unusual.

# Mark S. Miller (11 years ago)

If Date.prototype is a Date, then we need additional special logic to ensure that freezing it actually makes it immutable. Otherwise, we have a hard to plug global communications channel. This was the reason why RegExp.prototype.compile had an [[Extensible]] check, and why we can remove the check if RegExp.prototype is no longer a RegExp.

# Till Schneidereit (11 years ago)

On Thu, Jun 12, 2014 at 6:03 PM, C. Scott Ananian <ecmascript at cscott.net>

wrote:

FWIW, in Chrome 36:

> Date.prototype.toString()
"Invalid Date"
> ({ toString: Date.prototype.toString }).toString()
TypeError: this is not a Date object.
> Number.prototype.toString()
"0"
> String.prototype.toString()
""
> Boolean.prototype.toString()
"false"
> RegExp.prototype.toString()
"/(?:)/"

Yes, that's how it's supposed to work according to ES5, and does in all engines. Annex E.1 of ES6 notes that this changes.

# C. Scott Ananian (11 years ago)

On Thu, Jun 12, 2014 at 12:48 PM, Jason Orendorff <jorendorff at mozilla.com> wrote:

In any case, I doubt we have a choice. ES3-5 at least supported it. There is surely a Web page somewhere that calls .toString() on every object it can find, just because.

If you are concerned about compatibility, them Date.prototype.toString() should return "Invalid Date" (as Erik stated, in concrete pseudo-code, above), not the result of Object.prototype.toString(). Similarly, Boolean.prototype.toString() should return false, etc.

If we're changing the result of #toString, then we should just throw a TypeError, rather than return some arbitrary value. Specifically, I want Date#toString to consistently throw a TypeError if this is not a Date (or subclass), not to do some weird special case only for Date.prototype.

# Allen Wirfs-Brock (11 years ago)

On Jun 12, 2014, at 8:57 AM, Erik Arvidsson wrote:

On Thu Jun 12 2014 at 11:28:12 AM, C. Scott Ananian <ecmascript at cscott.net> wrote: It would be slightly more "JavaScripty" to have Date.prototype.[[DateValue]] exist, and be set to the epoch or some such.

+1

  1. Let date be the this value.
  2. If Type(date) is not Object then, throw a TypeError exception.
  3. If date does not have a [[DateValue]] internal slot, then let tv be NaN
  4. Else let tv be this time value.
  5. Return ToDateString(tv).

Allen, what is the benefit to do super.toString() instead?

This is really getting into how much and where we want to break legacy compatibility.

ES5 actually under specifies Data.prototype.toString. It doesn't say what then the this value is not a Date instance. Actually it says that the result is implementation dependent.

But, let's assume that it was in fact specified more like the current ES6 spec which your above proposed change is derived from. The same reasoning can be applicable to the Number, String, etc. which happen to be more precisely specified in ES5.

In that case, in ES5 (where Date.prototype is a Date instance) Date.prototyjpe.toString() should produce ToDateString(NaN), and Date.prototype.toString.call({ }); should throw because 'date' does not have a [[DateValue]] internal slot.

Your proposed change would return ToDateString(NaN) for both cases. So that preserves the ES5 level result when applied to Date.prototype but changes the result for any other non-Date object.

With my proposed solution they would both produce "[object Object]" for both cases. So it changes the result of both cases, relative to ES5.

So both solutions change the result produce for non-Date objects. I also change the results for Date.prototype while your solution preserves it. Which breaking change are we willing to to risk? In making the change to non-instance prototypes we placed a bet that nobody depended upon those prototypes being instances of their constructor. That presumably means that we were also betting that nobody is dependent upon the result you get when applying toString to those prototypes. From that perspective, the current spec. language is a good match to our bet. It changes what happens for toString applied to the prototype, but it preserves other Date toString behavior including throwing when Date.prototype.toString is applied to a non-Date object.

So, I think the current spec. best matches our consensus about changing these prototypes to non-consructor instances. I think my proposed alternative toString fall back pattern is more useful, but is a bigger breaking change. Are we willing to up the bet, or should we let it ride as is?

# André Bargull (11 years ago)

On Thu, Jun 12, 2014 at 12:48 PM, Jason Orendorff <jorendorff at mozilla.com, mail.mozilla.org/listinfo/es-discuss> wrote:

/ In any case, I doubt we have a choice. ES3-5 at least supported it. There is />/ surely a Web page somewhere that calls .toString() on every object it can />/ find, just because. /

Or a web page converts some value to a string using "" + someValue, in which case adding a toString() legacy mode is not sufficient, because valueOf() is actually invoked (well, except for Date.prototype because of its @@toPrimitive override). Does that mean valueOf() also needs to have a legacy mode to special case the prototype object?

# C. Scott Ananian (11 years ago)

On Thu, Jun 12, 2014 at 12:59 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

So, I think the current spec. best matches our consensus about changing these prototypes to non-consructor instances. I think my proposed alternative toString fall back pattern is more useful, but is a bigger breaking change. Are we willing to up the bet, or should we let it ride as is?

Just restating and naming the alternatives:

(a) #toString throws TypeError when given a non-instance. Changes Date#toString(), no change to Date#toString.call({}).

(b) #toString is generic; invokes Object#toString when given a non-instance. Changes both Date#toString() and Date#toString.call({}).

(c) #toString is generic; uses a "zero" value when given a non-instance. No change to Date#toString(); changes Date#toString.call({}).

(d) #toString returns a "zero" value when given a prototype, throws TypeError otherwise. No change to Date#toString() or Date#toString.call({}).

Option (a) is what is in the current spec. Options (b) and (c) make the toString method generic. Option (d) preserves compatibility to the greatest degree possible. --scott

ps. I prefer (a).

# Till Schneidereit (11 years ago)

On Thu, Jun 12, 2014 at 8:29 PM, C. Scott Ananian <ecmascript at cscott.net>

wrote:

On Thu, Jun 12, 2014 at 12:59 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

So, I think the current spec. best matches our consensus about changing these prototypes to non-consructor instances. I think my proposed alternative toString fall back pattern is more useful, but is a bigger breaking change. Are we willing to up the bet, or should we let it ride as is?

Just restating and naming the alternatives:

(a) #toString throws TypeError when given a non-instance. Changes Date#toString(), no change to Date#toString.call({}).

(b) #toString is generic; invokes Object#toString when given a non-instance. Changes both Date#toString() and Date#toString.call({}).

(c) #toString is generic; uses a "zero" value when given a non-instance. No change to Date#toString(); changes Date#toString.call({}).

(d) #toString returns a "zero" value when given a prototype, throws TypeError otherwise. No change to Date#toString() or Date#toString.call({}).

Option (a) is what is in the current spec. Options (b) and (c) make the toString method generic. Option (d) preserves compatibility to the greatest degree possible.

There is (e) #toString returns "[object Object]" when invoked on the (an) original Date.prototype (regardless of the Realm it came from). Otherwise, it throws when invoked on a non-instance.

This is what Allen and me proposed. And I think it's the best solution for two reasons:

  • I'd bet good money that (a) breaks the web so just isn't an option.
  • as Jason points out, stringifying an object should succeed for as many things as possible. The script authors can do what they want, but the builtins shouldn't throw if you stringify them.

As André points out, this affects valueOf, too.

# C. Scott Ananian (11 years ago)

On Thu, Jun 12, 2014 at 3:06 PM, Till Schneidereit <till at tillschneidereit.net> wrote:

On Thu, Jun 12, 2014 at 8:29 PM, C. Scott Ananian <ecmascript at cscott.net> wrote:

(a) #toString throws TypeError when given a non-instance. Changes Date#toString(), no change to Date#toString.call({}).

(b) #toString is generic; invokes Object#toString when given a non-instance. Changes both Date#toString() and Date#toString.call({}).

(c) #toString is generic; uses a "zero" value when given a non-instance. No change to Date#toString(); changes Date#toString.call({}).

(d) #toString returns a "zero" value when given a prototype, throws TypeError otherwise. No change to Date#toString() or Date#toString.call({}).

There is (e) #toString returns "[object Object]" when invoked on the (an) original Date.prototype (regardless of the Realm it came from). Otherwise, it throws when invoked on a non-instance.

This is what Allen and me proposed.

Allen said:

Your proposed change would return ToDateString(NaN) for both cases. So that preserves the ES5 level result when applied to Date.prototype but changes the result for any other non-Date object.

With my proposed solution they would both produce "[object Object]" for both cases. So it changes the result of both cases, relative to ES5.

...which is option (b). (Allen, correct me if I'm reading that wrong!)

# Allen Wirfs-Brock (11 years ago)

On Jun 12, 2014, at 12:14 PM, C. Scott Ananian wrote:

On Thu, Jun 12, 2014 at 3:06 PM, Till Schneidereit <till at tillschneidereit.net> wrote:

On Thu, Jun 12, 2014 at 8:29 PM, C. Scott Ananian <ecmascript at cscott.net> wrote:

(a) #toString throws TypeError when given a non-instance. Changes Date#toString(), no change to Date#toString.call({}).

(b) #toString is generic; invokes Object#toString when given a non-instance. Changes both Date#toString() and Date#toString.call({}).

(c) #toString is generic; uses a "zero" value when given a non-instance. No change to Date#toString(); changes Date#toString.call({}).

(d) #toString returns a "zero" value when given a prototype, throws TypeError otherwise. No change to Date#toString() or Date#toString.call({}).

There is (e) #toString returns "[object Object]" when invoked on the (an) original Date.prototype (regardless of the Realm it came from). Otherwise, it throws when invoked on a non-instance.

This is what Allen and me proposed.

Allen said:

Your proposed change would return ToDateString(NaN) for both cases. So that preserves the ES5 level result when applied to Date.prototype but changes the result for any other non-Date object.

With my proposed solution they would both produce "[object Object]" for both cases. So it changes the result of both cases, relative to ES5.

...which is option (b). (Allen, correct me if I'm reading that wrong!)

you're right.

Till, In the spec. we don't actually have a good way to identify any Date.prototype object from any Realm. We'd have to brand all Date.prototype objects in some way. It could be done, but it isn't something I would expect anybody to every bother to do for user defined classes. If such cross realm detection is actually important for Date why isn't it also important for the classes that a JS programmer defines.

# Till Schneidereit (11 years ago)

On Thu, Jun 12, 2014 at 9:30 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>

wrote:

On Jun 12, 2014, at 12:14 PM, C. Scott Ananian wrote:

On Thu, Jun 12, 2014 at 3:06 PM, Till Schneidereit <till at tillschneidereit.net> wrote:

On Thu, Jun 12, 2014 at 8:29 PM, C. Scott Ananian < ecmascript at cscott.net>

wrote:

(a) #toString throws TypeError when given a non-instance. Changes Date#toString(), no change to Date#toString.call({}).

(b) #toString is generic; invokes Object#toString when given a non-instance. Changes both Date#toString() and Date#toString.call({}).

(c) #toString is generic; uses a "zero" value when given a non-instance. No change to Date#toString(); changes Date#toString.call({}).

(d) #toString returns a "zero" value when given a prototype, throws TypeError otherwise. No change to Date#toString() or Date#toString.call({}).

There is (e) #toString returns "[object Object]" when invoked on the (an)

original Date.prototype (regardless of the Realm it came from). Otherwise,

it throws when invoked on a non-instance.

This is what Allen and me proposed.

Allen said:

Your proposed change would return ToDateString(NaN) for both cases. So that preserves the ES5 level result when applied to Date.prototype but changes the result for any other non-Date object.

With my proposed solution they would both produce "[object Object]" for both cases. So it changes the result of both cases, relative to ES5.

...which is option (b). (Allen, correct me if I'm reading that wrong!)

Oh, sorry, I missed that.

you're right.

Till, In the spec. we don't actually have a good way to identify any Date.prototype object from any Realm. We'd have to brand all Date.prototype objects in some way. It could be done, but it isn't something I would expect anybody to every bother to do for user defined classes. If such cross realm detection is actually important for Date why isn't it also important for the classes that a JS programmer defines.

Fair. (Ignoring the fact that all actual implementations probably do have a way to do this.) What about only special-casing Date.prototype from the current Realm, then? We have %DatePrototype% for that, and it probably covers the vast majority of the compatibility concerns.

# Mark S. Miller (11 years ago)

I like this list. I prefer #c.

  • We have previously succeeded at making previously non-generic methods generic. I think we could get away with #c.
  • It is easier for a normal JS programmer to do the equivalent of #c for most of their classes.
  • Doesn't requiring branding the builtin prototypes unnecessarily.
  • Works fine across Realms.
# C. Scott Ananian (11 years ago)

On Thu, Jun 12, 2014 at 3:56 PM, Mark S. Miller <erights at google.com> wrote:

I like this list. I prefer #c.

  • We have previously succeeded at making previously non-generic methods generic. I think we could get away with #c.
  • It is easier for a normal JS programmer to do the equivalent of #c for most of their classes.

FWIW, if a change to the spec is needed, I also favor (c).

Both generic options (b) and (c) have consistent behavior that can extend to #valueOf. In option (b) you would presumably invoke Object#valueOf and for option (c) you'd return the "zero" value.

# Till Schneidereit (11 years ago)

On Thu, Jun 12, 2014 at 10:42 PM, C. Scott Ananian <ecmascript at cscott.net>

wrote:

On Thu, Jun 12, 2014 at 3:56 PM, Mark S. Miller <erights at google.com> wrote:

I like this list. I prefer #c.

  • We have previously succeeded at making previously non-generic methods generic. I think we could get away with #c.
  • It is easier for a normal JS programmer to do the equivalent of #c for most of their classes.

FWIW, if a change to the spec is needed, I also favor (c).

Both generic options (b) and (c) have consistent behavior that can extend to #valueOf. In option (b) you would presumably invoke Object#valueOf and for option (c) you'd return the "zero" value.

Is this close-ish to a consensus? If so, we'd implement and land it on Nightly to get an idea of its web compatibility. It doesn't sound like there's a meaningful danger of implementing something seriously incompatible with what the spec is going to say - if (c) is roughly adopted.

# Mark S. Miller (11 years ago)

Trying #c on Nightly would provide us the information needed to bring #c to consensus.

# Allen Wirfs-Brock (11 years ago)

On Jun 17, 2014, at 2:10 AM, Till Schneidereit wrote:

On Thu, Jun 12, 2014 at 10:42 PM, C. Scott Ananian <ecmascript at cscott.net> wrote: On Thu, Jun 12, 2014 at 3:56 PM, Mark S. Miller <erights at google.com> wrote:

I like this list. I prefer #c.

  • We have previously succeeded at making previously non-generic methods generic. I think we could get away with #c.
  • It is easier for a normal JS programmer to do the equivalent of #c for most of their classes.

FWIW, if a change to the spec is needed, I also favor (c).

Both generic options (b) and (c) have consistent behavior that can extend to #valueOf. In option (b) you would presumably invoke Object#valueOf and for option (c) you'd return the "zero" value.

Is this close-ish to a consensus? If so, we'd implement and land it on Nightly to get an idea of its web compatibility. It doesn't sound like there's a meaningful danger of implementing something seriously incompatible with what the spec is going to say - if (c) is roughly adopted.

I'm not sure who introduced the idea that the Date.prototype should have a "zero value", but that is inconsistent with ES3&5 where the TimeValue of Date.prototype is NaN: www.ecma-international.org/ecma-262/5.1/#sec-15.9.5 If we went the (c) route it should presumably be modified to use NaN and not 0.

I have yet seen any evidence that there is an issue with valueOf. The original premise that TC39 agreed to is that we would make prototypes ordinary objects except for cases (like Array and Function) where there were known dependency on the legacy instance behavior. Has anybody actually observed code that depends upon Date.prototype.valueOf() returning NaN?

I'm also, not sure that we've seen an actual real world case that depends upon Date.prototype.toString() not throwing but I'm very sympathetic to the argument that it should be possible to toString any object.

That suggests that the issue here is broader than just legacy compatability for Date.prototype.toString. We have lots of new built-in constructors (Map, for example) whose prototypes are not instance objects. As of now we haven't defined specialized toString methods for them, but we might in the future and it would be nice to establish a pattern that user defined classes could follow. The approach of falling back to Object.prototype.toString when presented the wrong "kind" of object seems like a good general pattern for extending toString. That is essentially alternative (b).

So, here's mky proposal:

If somebody has evidence that there are actual real world dependencies upon Date.prototype.toString() producing ToDateString(NaN) then we should go with the NaN version of (c). Otherwise we should apply (b) to Date.prototype.toString. In either case, we should make all the other built-in toString methods that depend upon a specific kind of object also follow (b).

How about we test (b) for Date on Nightly?

# C. Scott Ananian (11 years ago)

On Tue, Jun 17, 2014 at 11:33 AM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

I'm not sure who introduced the idea that the Date.prototype should have a "zero value", but that is inconsistent with ES3&5 where the TimeValue of Date.prototype is NaN: www.ecma-international.org/ecma-262/5.1/#sec-15.9.5 If we went the (c) route it should presumably be modified to use NaN and not 0.

Sorry, I was using quotes around "zero value" on purpose to mean, "the appropriate value for the given type" (which is not actually 0). Date should be NaN, boolean should be false, etc. I hope most of those reading understood this.

# Mark Miller (11 years ago)

I am happy with #b as well, though I prefer #c. I also agree with C. Scott's interpretation of #c, to mean, appropriate degenerate value, which is generally the zero value, but is plausibly NaN for Date.

Whichever experiment Nightly tries first with a positive outcome, I expect that's what we'll do, since the difference between #b and #c is not large enough to be worth waiting for a second experiment.

# Till Schneidereit (11 years ago)

On Tue, Jun 17, 2014 at 6:07 PM, Mark Miller <erights at gmail.com> wrote:

I am happy with #b as well, though I prefer #c. I also agree with C. Scott's interpretation of #c, to mean, appropriate degenerate value, which is generally the zero value, but is plausibly NaN for Date.

Whichever experiment Nightly tries first with a positive outcome, I expect that's what we'll do, since the difference between #b and #c is not large enough to be worth waiting for a second experiment.

I don't much care which one we choose, to be honest. I kinda doubt that #b will have more compatibility issues than #c[1], and find conceptually cleaner by a thin margin: a non-Date object isn't an invalid Date, it's not a Date. But then again, if you don't want an object to be treated as a Date, don't call Date's toString on it? Implementation-wise, it's pretty much a wash, at least.

[1]: and for all I know there might be content out there that relies on an exception being thrown if Date#toString is used on a non-Date object ...

# Allen Wirfs-Brock (11 years ago)

On Jun 17, 2014, at 1:41 PM, Till Schneidereit wrote:

On Tue, Jun 17, 2014 at 6:07 PM, Mark Miller <erights at gmail.com> wrote: I am happy with #b as well, though I prefer #c. I also agree with C. Scott's interpretation of #c, to mean, appropriate degenerate value, which is generally the zero value, but is plausibly NaN for Date.

Whichever experiment Nightly tries first with a positive outcome, I expect that's what we'll do, since the difference between #b and #c is not large enough to be worth waiting for a second experiment.

I don't much care which one we choose, to be honest. I kinda doubt that #b will have more compatibility issues than #c[1], and find conceptually cleaner by a thin margin: a non-Date object isn't an invalid Date, it's not a Date. But then again, if you don't want an object to be treated as a Date, don't call Date's toString on it? Implementation-wise, it's pretty much a wash, at least.

I think the most important thing to test is changing Date.prototype to be a non-date (and for that matter changing all the other legacy built-ins that have changed to non-instances). That's the big bet and we should get feedback on.

As far as I can tell, toString being an issue is just a conjecture that hasn't been tested. So, you could even go ahead an implement the corresponding toString methods as currently stands in the ES6 spec. (throw for the wrong kind of object) which probably requires no change to their current implementation.

If we run into an actual toString issue we will have a better idea of which fix may be preferable.

# Till Schneidereit (11 years ago)

On Tue, Jun 17, 2014 at 11:27 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>

wrote:

On Jun 17, 2014, at 1:41 PM, Till Schneidereit wrote:

On Tue, Jun 17, 2014 at 6:07 PM, Mark Miller <erights at gmail.com> wrote:

I am happy with #b as well, though I prefer #c. I also agree with C. Scott's interpretation of #c, to mean, appropriate degenerate value, which is generally the zero value, but is plausibly NaN for Date.

Whichever experiment Nightly tries first with a positive outcome, I expect that's what we'll do, since the difference between #b and #c is not large enough to be worth waiting for a second experiment.

I don't much care which one we choose, to be honest. I kinda doubt that #b will have more compatibility issues than #c[1], and find conceptually cleaner by a thin margin: a non-Date object isn't an invalid Date, it's not a Date. But then again, if you don't want an object to be treated as a Date, don't call Date's toString on it? Implementation-wise, it's pretty much a wash, at least.

I think the most important thing to test is changing Date.prototype to be a non-date (and for that matter changing all the other legacy built-ins that have changed to non-instances). That's the big bet and we should get feedback on.

Agreed.

As far as I can tell, toString being an issue is just a conjecture that hasn't been tested. So, you could even go ahead an implement the corresponding toString methods as currently stands in the ES6 spec. (throw for the wrong kind of object) which probably requires no change to their current implementation.

I'm somewhat opposed to this for two reasons:

One is that I'm pretty sure that it won't be compatible, whereas I'm optimistic about making Date.prototype a non-Date. There are tens of thousands of people using Nightly as their default browser, and while that makes it a good first target for experiments like this, it also means that we shouldn't do experiments where we're not optimistic about the outcome.

The other is that I still think the standard library shouldn't contain objects that throw when they're string-ified or value-ified.

If we run into an actual toString issue we will have a better idea of which fix may be preferable.

How is that? The script-visible differences between #b and #c would mostly be that Date.prototype.toString would return "[Object Date]" for #b and (as it does now) "Invalid Date" for #c. It's not clear to me how testing the spec status quo would give us any more information about which one of these would be more compatible or preferable on other grounds.

# Brendan Eich (11 years ago)

I held back but can't any longer.

Till Schneidereit wrote:

As far as I can tell, toString being an issue is just a conjecture
that hasn't been tested. So, you could even go ahead an implement
the corresponding toString methods as currently stands in the ES6
spec. (throw for the wrong kind of object) which probably requires
no change to their current implementation.

I'm somewhat opposed to this for two reasons:

One is that I'm pretty sure that it won't be compatible, whereas I'm optimistic about making Date.prototype a non-Date. There are tens of thousands of people using Nightly as their default browser, and while that makes it a good first target for experiments like this, it also means that we shouldn't do experiments where we're not optimistic about the outcome.

The other is that I still think the standard library shouldn't contain objects that throw when they're string-ified or value-ified.

Agreed on both points.

Changing Date.prototype from how it has stringified for (now) over 19 years [1] takes unknown probabiltiy times non-trivial cost risk, and Firefox Nightly won't be enough to find the content that breaks (sorry). Other engines would need to test and even put into release channels the change, and then we'd hope some smart site bug diag guru figures out the problem, if there is a problem.

Best way to avoid this is to avoid it. What's the profit in (b)?

If we run into an actual toString issue we will have a better idea
of which fix may be preferable.

How is that? The script-visible differences between #b and #c would mostly be that Date.prototype.toString would return "[Object Date]"

(Lower-case "object" there.)

for #b and (as it does now) "Invalid Date" for #c. It's not clear to me how testing the spec status quo would give us any more information about which one of these would be more compatible or preferable on other grounds.

Just use "Invalid Date" and get on with more important work. My 2 cents.

/be

[1] SpiderMonkey session:

js> Date.prototype.toString() "Invalid Date" js> Date.prototype.toSource() "(new Date(NaN))"

# Jason Orendorff (11 years ago)

On Wed, Jun 18, 2014 at 1:04 PM, Brendan Eich <brendan at mozilla.org> wrote:

Just use "Invalid Date" and get on with more important work. My 2 cents.

Endorse. Can we get that added to the spec as normative text? ;-)

# Allen Wirfs-Brock (11 years ago)

On Jun 18, 2014, at 11:04 AM, Brendan Eich wrote:

I held back but can't any longer.

Till Schneidereit wrote:

As far as I can tell, toString being an issue is just a conjecture that hasn't been tested. So, you could even go ahead an implement the corresponding toString methods as currently stands in the ES6 spec. (throw for the wrong kind of object) which probably requires no change to their current implementation.

I'm somewhat opposed to this for two reasons:

One is that I'm pretty sure that it won't be compatible, whereas I'm optimistic about making Date.prototype a non-Date. There are tens of thousands of people using Nightly as their default browser, and while that makes it a good first target for experiments like this, it also means that we shouldn't do experiments where we're not optimistic about the outcome.

The other is that I still think the standard library shouldn't contain objects that throw when they're string-ified or value-ified.

Agreed on both points.

Changing Date.prototype from how it has stringified for (now) over 19 years [1] takes unknown probabiltiy times non-trivial cost risk, and Firefox Nightly won't be enough to find the content that breaks (sorry). Other engines would need to test and even put into release channels the change, and then we'd hope some smart site bug diag guru figures out the problem, if there is a problem.

Best way to avoid this is to avoid it. What's the profit in (b)?

Mostly about establishing the pattern for what should be done for other toString methods that are applied to the wrong kind of object or a non-instance prototype. Not every object that could get a custom toString has a natural "0-value".

For example, what should Symbol.prototype.toString() do? (b) sounds like a good choice for it. (c) is certainly ok, for Date.prototype but but we have other cases that need to be addressed.

# Brendan Eich (11 years ago)

Allen Wirfs-Brock wrote:

Mostly about establishing the pattern for what should be done for other toString methods that are applied to the wrong kind of object or a non-instance prototype. Not every object that could get a custom toString has a natural "0-value".

Don't let the tail wag the dog here.

That's on top of "avoid risk you can't size easily and don't need".

For example, what should Symbol.prototype.toString() do? (b) sounds like a good choice for it. (c) is certainly ok, for Date.prototype but but we have other cases that need to be addressed.

Symbol.prototype could even throw, it's such an outlier. Throw if no "zero" might go a long way, cover most of the dog.

# Mark S. Miller (11 years ago)

On Wed, Jun 18, 2014 at 2:38 PM, Brendan Eich <brendan at mozilla.org> wrote:

[...] Throw if no "zero" might go a long way, cover most of the dog.

Is there a cartoonist in the house?

# Allen Wirfs-Brock (11 years ago)

On Jun 18, 2014, at 2:38 PM, Brendan Eich wrote:

For example, what should Symbol.prototype.toString() do? (b) sounds like a good choice for it. (c) is certainly ok, for Date.prototype but but we have other cases that need to be addressed.

Symbol.prototype could even throw, it's such an outlier. Throw if no "zero" might go a long way, cover most of the dog.

The spec. current says throw for this Symbol.prototype case. The (reasonable) opposing view is that toString should never throw. Other than the protoype-is-not-an-instance it all about unlikely edge cases where toString methods are applied to the wrong kind of object.

# Brendan Eich (11 years ago)

Allen Wirfs-Brock wrote:

The spec. current says throw for this Symbol.prototype case. The (reasonable) opposing view is that toString should never throw. Other than the protoype-is-not-an-instance it all about unlikely edge cases where toString methods are applied to the wrong kind of object.

js> Object.create(null).toString()

typein:1:0 TypeError: Object.create(...).toString is not a function

It happens. Better to catch that error (Symbol.prototype flowing into an implicit conversion) early?

# Allen Wirfs-Brock (11 years ago)

On Jun 18, 2014, at 4:12 PM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

The spec. current says throw for this Symbol.prototype case. The (reasonable) opposing view is that toString should never throw. Other than the protoype-is-not-an-instance it all about unlikely edge cases where toString methods are applied to the wrong kind of object.

js> Object.create(null).toString() typein:1:0 TypeError: Object.create(...).toString is not a function

It happens. Better to catch that error (Symbol.prototype flowing into an implicit conversion) early?

which is pretty much the approach the ES6 spec. has taken WRT toString up to now. Tension between catching invalid iplicit toString conversions and reliable toString for debugging.

At any rate, run time tools can really depend upon toString working, They probably should use something like:

function reliableToString(obj) { try {return obj.toString()} catch (e) { try {return {}.toString.call(obj)} catch (f) { return "[ object ???]"} } } }

# Brendan Eich (11 years ago)

Allen Wirfs-Brock wrote:

"[ object ???]"

"[object WTF]"

:

# Till Schneidereit (11 years ago)

On Thu, Jun 19, 2014 at 1:39 AM, Allen Wirfs-Brock <allen at wirfs-brock.com>

wrote:

On Jun 18, 2014, at 4:12 PM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

The spec. current says throw for this Symbol.prototype case. The (reasonable) opposing view is that toString should never throw. Other than the protoype-is-not-an-instance it all about unlikely edge cases where toString methods are applied to the wrong kind of object.

js> Object.create(null).toString() typein:1:0 TypeError: Object.create(...).toString is not a function

It happens. Better to catch that error (Symbol.prototype flowing into an implicit conversion) early?

which is pretty much the approach the ES6 spec. has taken WRT toString up to now. Tension between catching invalid iplicit toString conversions and reliable toString for debugging.

I'd posit that there are three different cases for toString:

  1. (explictly or implicitly) calling foo.toString, where foo is any of the objects that, by default, exist in a JS global without any user code having run
  2. (explicitly or implicitly) calling foo.toString, where foo is some content script-generated object
  3. explicitly calling bar.toString.call(foo), where foo and bar are arbitrary objects

The current (ES5) state of things is that 1) can never throw, while both 2) and 3) might. The current ES6 draft would move 1) over into the latter camp. I think that is the crucial thing to prevent. It's just wrong for the language to essentially say "here's a bunch of objects. They have methods. But don't call all of them. Finding out which ones aren't allowed to be called is left as an excercise to the reader."

Theoretically, Scott's #d still hold the most appeal to me. However, I don't think it works in a multi-Realm world, hence "theoretically". Given that, it's #b or #c, where, again, I don't much care which one it'll be.

At any rate, run time tools can really depend upon toString working, They probably should use something like:

function reliableToString(obj) { try {return obj.toString()} catch (e) { try {return {}.toString.call(obj)} catch (f) { return "[ object ???]"} } } }

True. It's not clear to me that the web as a whole knows this, though, and the current state of the spec might teach it the hard way.

# Andrea Giammarchi (11 years ago)

Can i bring back the good old JScript "unknown"? :P

Sent from my Windows Phone From: Brendan Eich Sent: ‎6/‎18/‎2014 17:15 To: Allen Wirfs-Brock Cc: Mark S. Miller; Mark Miller; Erik Arvidsson; Jason Orendorff; es-discuss at mozilla.org list Subject: Re: Standard builtins' prototypes and toString Allen Wirfs-Brock wrote:

"[ object ???]"

"[object WTF]"

:

# Allen Wirfs-Brock (11 years ago)

On Jun 19, 2014, at 3:18 AM, Till Schneidereit wrote:

On Thu, Jun 19, 2014 at 1:39 AM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

On Jun 18, 2014, at 4:12 PM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

The spec. current says throw for this Symbol.prototype case. The (reasonable) opposing view is that toString should never throw. Other than the protoype-is-not-an-instance it all about unlikely edge cases where toString methods are applied to the wrong kind of object.

js> Object.create(null).toString() typein:1:0 TypeError: Object.create(...).toString is not a function

It happens. Better to catch that error (Symbol.prototype flowing into an implicit conversion) early?

which is pretty much the approach the ES6 spec. has taken WRT toString up to now. Tension between catching invalid iplicit toString conversions and reliable toString for debugging.

I'd posit that there are three different cases for toString:

  1. (explictly or implicitly) calling foo.toString, where foo is any of the objects that, by default, exist in a JS global without any user code having run
  2. (explicitly or implicitly) calling foo.toString, where foo is some content script-generated object
  3. explicitly calling bar.toString.call(foo), where foo and bar are arbitrary objects

The current (ES5) state of things is that 1) can never throw, while both 2) and 3) might. The current ES6 draft would move 1) over into the latter camp. I think that is the crucial thing to prevent. It's just wrong for the language to essentially say "here's a bunch of objects. They have methods. But don't call all of them. Finding out which ones aren't allowed to be called is left as an excercise to the reader."

Theoretically, Scott's #d still hold the most appeal to me. However, I don't think it works in a multi-Realm world, hence "theoretically". Given that, it's #b or #c, where, again, I don't much care which one it'll be.

Or even in a single Realm world, it isn't clear how: (class extends Date {}).prototype.toString() would recognize that is is dealing with a prototype rather than a Date instance.

I basically agree with you conclusion. An the conserve thing to do is to try to minimize legacy compat. To me, that says that for Date, Number, Boolean, String, RegExp we should do (c). For new kinds of objects that don't exist in ES5 and which have internal state dependent toStrings we should do (b).

I'll update the spec. accordingly.

Note that for the (c) cases this introduces a different breaking change: ES5 specifies that String.prototype.toString.call({}) throws a TypeError but the (c) based change will return the empty string for that case. Hopefully, that is a change the web can live with.

# C. Scott Ananian (11 years ago)

While we're on the topic, shall we discuss valueOf()? Should it get the same treatment?

# Till Schneidereit (11 years ago)

On Thu, Jun 19, 2014 at 7:46 PM, C. Scott Ananian <ecmascript at cscott.net>

wrote:

While we're on the topic, shall we discuss valueOf()? Should it get the same treatment? --scott

Oh, yes: I always considered the toString discussion to really be about valueOf, too.

On Jun 19, 2014 1:28 PM, "Allen Wirfs-Brock" <allen at wirfs-brock.com> wrote:

On Jun 19, 2014, at 3:18 AM, Till Schneidereit wrote:

On Thu, Jun 19, 2014 at 1:39 AM, Allen Wirfs-Brock <allen at wirfs-brock.com

wrote:

On Jun 18, 2014, at 4:12 PM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

The spec. current says throw for this Symbol.prototype case. The (reasonable) opposing view is that toString should never throw. Other than the protoype-is-not-an-instance it all about unlikely edge cases where toString methods are applied to the wrong kind of object.

js> Object.create(null).toString() typein:1:0 TypeError: Object.create(...).toString is not a function

It happens. Better to catch that error (Symbol.prototype flowing into an implicit conversion) early?

which is pretty much the approach the ES6 spec. has taken WRT toString up to now. Tension between catching invalid iplicit toString conversions and reliable toString for debugging.

I'd posit that there are three different cases for toString:

  1. (explictly or implicitly) calling foo.toString, where foo is any of the objects that, by default, exist in a JS global without any user code having run
  2. (explicitly or implicitly) calling foo.toString, where foo is some content script-generated object
  3. explicitly calling bar.toString.call(foo), where foo and bar are arbitrary objects

The current (ES5) state of things is that 1) can never throw, while both 2) and 3) might. The current ES6 draft would move 1) over into the latter camp. I think that is the crucial thing to prevent. It's just wrong for the language to essentially say "here's a bunch of objects. They have methods. But don't call all of them. Finding out which ones aren't allowed to be called is left as an excercise to the reader."

Theoretically, Scott's #d still hold the most appeal to me. However, I don't think it works in a multi-Realm world, hence "theoretically". Given that, it's #b or #c, where, again, I don't much care which one it'll be.

Or even in a single Realm world, it isn't clear how: (class extends Date {}).prototype.toString() would recognize that is is dealing with a prototype rather than a Date instance.

I basically agree with you conclusion. An the conserve thing to do is to try to minimize legacy compat. To me, that says that for Date, Number, Boolean, String, RegExp we should do (c). For new kinds of objects that don't exist in ES5 and which have internal state dependent toStrings we should do (b).

I'll update the spec. accordingly.

Note that for the (c) cases this introduces a different breaking change: ES5 specifies that String.prototype.toString.call({}) throws a TypeError but the (c) based change will return the empty string for that case. Hopefully, that is a change the web can live with.

Same for Date.prototype.toString({}) as mentioned above, yes. I'm cautiously optimistic about these changes.

# Allen Wirfs-Brock (11 years ago)

On Jun 19, 2014, at 10:46 AM, C. Scott Ananian wrote:

While we're on the topic, shall we discuss valueOf()? Should it get the same treatment? --scott

I don't think so. To me, toString is a special case because it is explicitly applied so often, particularly for debugging purposes.

But valueOf is primarily invoked for implicit conversions in expressions.

Like I described earlier in this thread, TC39's assumption when it agreed to this ES design change was that the web did not generally depend upon using these existing ES3 generation (or earlier) prototypes as instance objects. In the case where we know that wasn't true (Function.prototype) we intentionally did not change the prototype to being a non-instance object. We also agreed if we discovered other such dependencies we would change those prototypes back to being instance objects.

Discovering that one of these prototypes objects were being routinely used in expression contexts where valueOf was being implicitly applied would be evidence that that prototype needed to be handled in the legacy manner. But, we don't have any new evidence of that being the case. Until there is some evidence, there isn't any reason to second guess the original analysis and TC39's decision.

# Till Schneidereit (11 years ago)

On Thu, Jun 19, 2014 at 8:16 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>

wrote:

On Jun 19, 2014, at 10:46 AM, C. Scott Ananian wrote:

While we're on the topic, shall we discuss valueOf()? Should it get the same treatment? --scott

I don't think so. To me, toString is a special case because it is explicitly applied so often, particularly for debugging purposes.

But valueOf is primarily invoked for implicit conversions in expressions.

Like I described earlier in this thread, TC39's assumption when it agreed to this ES design change was that the web did not generally depend upon using these existing ES3 generation (or earlier) prototypes as instance objects. In the case where we know that wasn't true (Function.prototype) we intentionally did not change the prototype to being a non-instance object. We also agreed if we discovered other such dependencies we would change those prototypes back to being instance objects.

Discovering that one of these prototypes objects were being routinely used in expression contexts where valueOf was being implicitly applied would be evidence that that prototype needed to be handled in the legacy manner. But, we don't have any new evidence of that being the case. Until there is some evidence, there isn't any reason to second guess the original analysis and TC39's decision.

Ok, we'll try this in Nightly for Date.prototype and see if the assumption holds. If it does, we'll continue with the rest.

I don't really know if Date.prototype is a good canary, but it's what I have a mostly-finished patch for, and it doesn't seem to make too much of a difference.