This is an experimental technology, part of the Harmony (ECMAScript 6) proposal.
Because this technology's specification has not stabilized, check the compatibility table for usage in various browsers. Also note that the syntax and behavior of an experimental technology is subject to change in future version of browsers as the spec changes.
The Iterable Interface
In the strict sense, The Iterable in ECMAScript 6 is an interface (or in other words, a protocol), not a type of object like Array
or Map
.
The Iterable interface includes the following property:
Property | value | Requirements |
---|---|---|
@@iterator | A zero arguments function that returns an object. | The function returns an object that conforms to the Iterator interface. |
The @@iterator
is a specification name for the Well-known Symbol
Symbol.iterator
.
Iterable objects
Any object that implements the Iterable interface is called an iterable object (iterable for short).More specifically, an iterable object has to have a Symbol.iterator
keyed property whose value is a function that returns an object that supports the Iterator interface.
For example, a string is an iterable:
var aString = "hello" typeof aString[Symbol.iterator] // "function" aString[Symbol.iterator]() + "" // "[object String Iterator]" [...aString] // ["h", "e", "l", "l", "o"]
Builtin iterables
String
, Array
, Map
, Set
and Generator
objects are all builtin iterables, because the prototype objects of them all have an @@
iterator
method. e.g., String.prototype.@@iterator
, Array.prototype.@@iterator
, Generator.prototype.@@iterator
and so on.
The arguments
object is also a builtin iterable, but it has a self property @@iterator
, not a prototype one.
User-defined iterables
We can make our own iterables like this:
var myIterable = {} myIterable[Symbol.iterator] = function* () { yield 1; yield 2; yield 3; }; [...myIterable] // [1, 2, 3]
Builtin APIs need iterables
Map([iterable])
, WeakMap([iterable])
, Set([iterable])
and WeakSet([iterable])
:
var myObj = {} new Map([[1,"a"],[2,"b"],[3,"c"]]).get(2) // "b" new WeakMap([[{},"a"],[myObj,"b"],[{},"c"]]).get(myObj) // "b" new Set([1, 2, 3]).has(3) // true new Set("123").has("2") // true new WeakSet(function*() { yield {}; yield myObj; yield {}; }()).has(myObj) // true
and Promise.all(iterable)
, Promise.race(iterable)
, Array.from()
Syntaxes need iterables
for-of, spread, yield*, destructing
for(let value of ["a", "b", "c"]){ console.log(value) } // "a" // "b" // "c" [..."abc"] // ["a", "b", "c"] function* gen(){ yield* ["a", "b", "c"] } gen().next() // { value:"a", done:false } [a, b, c] = new Set(["a", "b", "c"]) a // "a"
Non-well-formed iterables
If an iterable's @@iterator
method doesn't return an iterator object, then it's a non-well-formed iterable, using it as such is likely to result in runtime exceptions or buggy behavior:
var nonWellFormedIterable = {} nonWellFormedIterable[Symbol.iterator] = () => 1 [...nonWellFormedIterable] // TypeError: [] is not a function
A generator object is an iterator or an iterable?
The answer is, both are correct:
var aGeneratorObject = function*(){ yield 1; yield 2; yield 3; }() typeof aGeneratorObject.next // "function", because it has a next method, so it's an iterator typeof aGeneratorObject[Symbol.iterator] // "function", because it has an @@iterator method, so it's an iterable aGeneratorObject[Symbol.iterator]() === aGeneratorObject // true, because its @@iterator method return its self (an iterator), so it's an well-formed iterable [...aGeneratorObject] // [1, 2, 3]