Why does Array.from accept non-iterable arraylikes?

Jason Orendorff jason.orendorff at gmail.com
Tue Jun 25 08:30:18 PDT 2013


On Mon, Jun 24, 2013 at 11:43 AM, Rick Waldron <waldron.rick at gmail.com> wrote:
> While all builtin arraylikes are also iterable now, arraylikes created by
> non-built-in (assuming we both use "built-in" to refer to objects defined by
> ES) may not implement iterator protocol. Array.from, like
> Function.prototype.apply, will allow those objects to be turned into an
> array, eg. NodeList, DOMTokenList or jQuery objects.

NodeList and DOMTokenList are iterable in Firefox. NodeList is even
provisionally iterable per spec at the moment. It's marked as
`[ArrayClass]`:

  http://dom.spec.whatwg.org/#interface-nodelist

which means instances inherit from Array.prototype, including its
@@iterator method.

The infrastructure to make interfaces iterable, even without
[ArrayClass], also already exists in WebIDL:

  http://dev.w3.org/2006/webapi/WebIDL/#idl-iterators

In short, I wouldn't worry about other specs or software failing to
adopt iterability. The awful truth is, every other party to the
problem moves many times faster than new JS features propagate to the
last 10% of Web users. I'd be astonished if jQuery objects weren't
iterable long before ES6 features are considered generally available.
Iterability support is just a few lines of code, and jQuery users will
want to use Array.from!

> A cowpath that
> Array.from was intended to clear and pave was
> [].slice.call(arrayLike)/Array.prototype.slice.call(arrayLike).

Array.from should still satisfy that use case. Note that as proposed
currently, Array.from already isn't totally polyfillable; it is only
approxi-fill-able; dropping support for arraylikes will not change
that.

> Also, web
> devs will be able to use a polyfilled Array.from in code that must run in
> browsers that don't/won't support spread (ie. older non-updating browsers).

Ooh, interesting. Let's think this through.

In browsers that have neither iteratorSymbol nor array-spread syntax,
polyfills won't have access to either side of the iteration protocol.
They can't provide or consume iterables. So they'll have to either
"approxifill" Array.from or not try. I think they will not try.

I can imagine them using the arraylike protocol, with some extra cruft
behind the scenes to pick up particular non-arraylike iterables, such
as instances of their own Map and Set polyfills. That is, polyfills
*might* try to approx-i-fill Array.from using various workarounds; but
I'm not convinced we should carry those workarounds forward into ES6.

However I think the right answer is to make Array.from polyfillable.

If we change the name of the method from a symbol to plain
.iterator(), the iteration protocol will be fully accessible in pure
ES1-5 code. Functions that produce or consume iterables would become
accurately polyfillable. Array.from() would be polyfillable. The Map
and Set constructors. Polyfills would also be able to monkeypatch DOM
classes to make them iterable in old browsers.

There's a benefit for pure ES6 code too; it would be easier to make a
class iterable:

    class Table {
        *iterator() {
            ...
        }
    }

(Currently an extra scrap of code outside the class is needed, since
we don't have syntactic sugar for symbol-named methods.)

And jQuery could just add `iterator: [].iterator,` to an object
literal somewhere. Literally one line of code to make jQuery objects
iterable in browsers that have iteration support. No try-catch, eval,
or import needed.

I think TC39 made @@iterator a symbol on the theory that users would
want to build iterable Proxy-based string-key maps that would support
property-access syntax. I wonder if that rationale holds anymore, now
that there's the invoke trap.

-j


More information about the es-discuss mailing list