Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the technical definition of a Javascript iterable and how do you test for it?

I've been implementing a useful subclass of the ES6 Set object. For many of my new methods, I want to accept an argument that can be either another Set or an Array, or really anything that I can iterate. I've been calling that an "iterable" in my interface and just use .forEach() on it (which works fine for a Set or an Array. Example code:

// remove items in this set that are in the otherIterable
// returns a count of number of items removed
remove(otherIterable) {
    let cnt = 0;
    otherIterable.forEach(item => {
        if (this.delete(item)) {
            ++cnt;
        }
    });
    return cnt;
}

Or

// add all items from some other iterable to this set
addTo(iterable) {
    iterable.forEach(item => {
        this.add(item);
    });
}

But, I suspect I may be not really supporting any iterable in the way that ES6 defines it so I'm interested in what the real definition of a Javascript iterable is using the term as the ES6 specification does?

How do you test for it in ES6 Javascript?

How should you iterate a generic iterable?

I've found phrases like this in the ES6 specification:

If the parameter iterable is present, it is expected to be an object that implements an @@iterator method that returns an iterator object that produces a two element array-like object whose first element is a value that will be used as a WeakMap key and whose second element is the value to associate with that key.

But, that refers to an @@iterator method which I don't seem to be able to access via that property name.

like image 335
jfriend00 Avatar asked Jan 10 '17 00:01

jfriend00


1 Answers

What is the real definition of a Javascript iterable using the term as the ES6 specification does?

§25.1.1.1 defines "The Iterable Interface".

They're objects with a Symbol.iterator-keyed method that returns a valid Iterator (which in turn is an object expected to behave as it should according to §25.1.1.2).

How do you test for it in ES6 Javascript?

We cannot test what the @@iterator method returns without calling it, and we cannot test whether the result conforms to the Iterator interface without trying to run it. The best bet would be to do

function looksIterable(o) {
    return typeof o[Symbol.iterator] == "function";
}

however I wouldn't usually test for this but simply let it fail with an exception when it's not iterable.

How should you iterate a generic iterable?

Don't use forEach. (In fact, never use forEach anywhere in ES6).

The proper way to iterate is a for (… of …) loop. It does all the checking for iterability (using the abstract GetIterator operation and running (and even closing) the iterator, and throws appropriate TypeErrors when used on non-iterable values.

like image 105
Bergi Avatar answered Sep 20 '22 10:09

Bergi