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]