Array.isArray(new Proxy([], {})) should be false (Bug 1096753)

Allen Wirfs-Brock allen at wirfs-brock.com
Thu Nov 13 11:31:05 PST 2014


On Nov 12, 2014, at 12:12 PM, Tom Van Cutsem <tomvc.be at gmail.com> wrote:

> 2014-11-12 19:53 GMT+01:00 Rick Waldron <waldron.rick at gmail.com>:
> I agree and I want to know if you think this is worth revisiting once more? Next meeting's agenda?
> 
> I don't think we've ever discussed this issue before during a TC39 meeting (but I didn't attend quite a few, so I may be wrong). Adding it to the agenda for next meeting seems good to me.
> 

Yes, this has been discussed at TC39 meetings although I’d have to go search notes for a record of it.  Regardless, I’ll try to summarize the logic that lead to the current decision.

First Array.isArray was introduced in ES5 to provide a reliable way to detect Array instances that works regardless of what realm created the instance. In ES5, Array.isArray  is specified as a test whether the [[Class]] internal property has the the value “Array”.  In ES5 the only objects that have that [[Class]] values are objects created use the Array constructor or using an Array literal (or internally instantiated objects created “as if” by `new Array`).  Any such object responds true for Array.isArray even if its [[Prototype]] is set (using __proto__) to something different than Array.prototype. 

All ES5 objects for which Array.isArray answers true have exotic `length` behavior where adding an index property beyond the current `length` increases the value of `length` and deleting training index properties decrease the value of `length`.

ES6 eliminates the [[Class]] internal properties, but the equivalent is any object that is an “exotic array object’ .  Any “exotic array object” is an exotic object whose [[DefineProperty]] internal method maintains the `length` property invariant described above.  ES6 specifies that Array.isArray answers true only for exotic array objects. Given any ES5-level ES code, the ES6 spec. will produce exactly the same result of Array.isArray as was produced by ES5.

In deciding the ES6 behavior for Array.isArray we considered at least four significant issues:
1) Backwards compatibility with ES5 level code that uses ES5.1.  It must produce the same result for all such code. This includes code that does __proto__ hacking.

2) ES6 subclassing.  Given `let sa = new (class extends Array {});`  what should be the value of `Array.isArray(sa)` ?  This is something we explicitly talked about at TC39 meetings and agreed that the answer should be true (except when…).  However, there are really two kinds of subclass to consider. Subclasses of Array that automatically inherit the creation of exotic array object instances and subclasses of Array that explicit over-ride in their constructor to produce some other kind of instance object (an ordinary object or perhaps some other kind of exotic object or a Proxy).  Note this is roughly equivalent to ad hoc ES5 subclassing where you might either use __proto__ to change the [[Prototype]] of an array instance to something other than Array.prototype or where you might create an ordinary object (not an Array instance) and set its [[Prototype]] to Array.prototype in order to inherit Arrray.prototype methods. In the TC39 discussions we concluded that the exotic array based isArray definition gives reasonable answers for both ES5 and Es6 subclassing.

3) Proxies. Should Array.isArray treat proxy instances specially? To answer this question we need to think about what a ES programmer actually thinks Array.isArray means? The meaning that ES5 established is that Array.isArray returning true means that the object observably maintains the array `length` invariant. There is no general way to determine whether this invariant is true for an arbitrary proxy.  Checking if the proxy’s target object is an exotic array object isn’t sufficient to say isArray is true because the defineProperty/get/set/delete traps might not maintain the invariant.  Similar, checking that the object is an ordinary object isn’t suffiient for isArray to say false because appropriate the handler may impose the array `length` invariants upon an ordinary object.

4)Self-hosting Array. An ES6 goal was to allow self-hosting of built-ins using Proxy where necessary to implement self-hosted exotic objects. A implementation could self-host Array by using a Proxy that targeted an ordinary object and a handler that maintained the `length` invariants. However, if it did this, it would also have to provide an implementation of Array.isArray that was able to recognize those Proxy based self-hosted Array instances.  It might by using some sort of private tagging mechanism of Array instances (for example via a WeakSet) that it’s Array.isArray would recognize.

The current ES6 definition still seems like the best definition, if you accept that Array.isArray means "does this object maintain the `length` invariant?" Tunneling through a Proxy and testing if its target is an exotic array object isn’t an adequate fix, because of the reasons mentioned above.

In the end, this is really about what JS programmers think Array.isArray means and why they would want to call it.  One of the main motivating use cases during ES5 development was the JSON behavior of stringifying Array instances differently from non-Array instances. In retrospect, Array.isArray is improbably not the best way to address that use case.  If we were doing it over in ES6 I probably would have defined an @@stringifyAsArray property on Array.prototype and made JSON.stringify sensitive to the presence of that property. 

What are other reasonable use cases for Array,isArray?

Allen









> regards,
> Tom
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141113/2b8f439c/attachment-0001.html>


More information about the es-discuss mailing list