I’d urge us to be more cautious about throwing away, or postponing, the solid 
win that is @@toStringTag. This is something we’ve wanted for a long time.

I’ve recently gotten several requests for jsdom to support DOM-compatible 
return values for Object.prototype.toString [1] [2], and each time I’ve told 
the requestors that we’re going to have to wait for proper @@toStringTag 
support [3] [4] to get a solid emulation. Given that @@toStringTag looks to be 
on-track to ship within one or two more V8 releases, I was very excited to be 
able give those users what they’re asking for finally.

Even more so, on the W3C TAG we’ve recently been working on an “Extensible Web 
Report Card” [5], regarding how well the platform can be explained. One of our 
best success stories is “Explaining the DOM via JavaScript” [6], which talks 
about how with the advent of ES6 (proxies, weak maps, and more) we can finally 
self-host all of the DOM (with the exception of `document.all`, of course). It 
would be a shame to move from virtually-100% self-hostable to 0% in one 
decision!

---

As I’ve tried to emphasize, although I can understand where Jordan’s concern 
comes from, I don’t think it’s a compelling one. He even states:

> The goal, in my opinion, of Object.prototype.toString checking is not 
> security - it's avoiding common developer hazards.

Given this, I do not think we should be worried at all about the consequences 
of @@toStringTag. The scenario he envisions---where someone overrides 
Array.prototype[Symbol.toStringTag]---is far outside the realm of "common 
developer hazards." Saying that code intended for

>  "is this likely to behave like an array" without ducktyping or feature 
> testing

is not resilient in the face of people modifying the built-in prototypes seems 
*absolutely fine*. As Allen has pointed out, the test is not reliable at all: 
even if you freeze `Array.prototype[Symbol.toStringTag]`, I can override 
`Array.prototype` and `Array.prototype.__proto__` to cause "is this likely to 
behave like an array" to be false no matter what `Object.prototype.toString` 
returns. (If you plan on saving away the values of those and using them on 
array literals only, then I'm just going to install a non-configurable throwing 
setter on `Array.prototype[0]`, and you still won't be able to use them 
effectively. This is not a winnable game!) In Jordan's message, he states that 
he's not concerned about these kinds of scenarios. Instead,
 
> I'm concerned about people passing me things and *not realizing* that it's 
> not what I wanted them to pass

But in this case you should *definitely* not be concerned about code that has 
overwritten `Array.prototype[Symbol.toStringTag]`, as it's so far outside the 
realm of expected API usage that it's clearly purposeful!

---

We've been through this "should we lock it down" dance a couple times just in 
my time on the committee. The most prominent analogy is in my mind 
Function.prototype.length, which we specifically *un*-locked to be 
user-configurable---in the same way as @@toStringTag, by making it non-writable 
but configurable.

And I think in general this is a good decision. For every built-in property or 
method, you can invent a use case of similar plausibility to "is this likely to 
behave like an array" that would advise toward locking down that property or 
method. For example, people often serialize out functions with 
Function.prototype.toString and parse/manipulate them---and we don't want that 
to be broken if someone modifies Function.prototype.toString "without 
realizing" the consequences, so we should freeze Function.prototype.toString! 
But the fallacy here is believing that we need these use cases to be resilient 
in the face of modified built-ins.

I understand the fact that un-locking toString-tags is a new ability in ES6 
that ES5 didn't have. But we have lots of these, from Function.prototype.length 
onward [7]. (Another good analogy is the various changes from magic own-data 
properties to configurable getters---now people can override those getters and 
break various expectations.) Nothing of value is lost, because if a collection 
of code messes with built-in prototypes, all of your attempted invariants 
(like, `Object.prototype.toString.call(a) === "[object Array]"` => 
`Array.prototype.forEach` will work on the object) are up in the air anyway. 
Much more often we're going to have people modifying them who *know what 
they're doing*, and are prepared to reap benefits from them.

[1]: https://github.com/tmpvar/jsdom/issues/766
[2]: https://github.com/tmpvar/jsdom/issues/935
[3]: https://github.com/tmpvar/jsdom/pull/945#issuecomment-62369868
[4]: https://github.com/tmpvar/jsdom/pull/970#issuecomment-65869082
[5]: http://extensiblewebreportcard.org/
[6]: https://extensiblewebreportcard.org/#toc-7
[7]: 
https://people.mozilla.org/~jorendorff/es6-draft.html#sec-in-the-6th-edition

_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to