2

I'm learning about Iterables from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators, and it clearly states that Array is Iterable. Inspecting it in chrome console, we can see it has Symbol.iterator, and we can run for..of on it.

But Arrays don't seem to follow the Iterator pattern of having a next() method, which I kind of get, because you can't reset an iterator and that would be severely limiting on an array.

But I'm having trouble identifying from the docs what Iterables have the full implementation (next()) and which don't, or I'm missing a basic concept here.

What am I missing?

Tom Auger
  • 19,421
  • 22
  • 81
  • 104
  • [*"It is not possible to know reflectively whether a particular object implements the iterator protocol..."*](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#The_iterator_protocol) – zer00ne May 15 '19 at 15:42

2 Answers2

6

I think the thing you are missing is that there is a difference between an iterator and something that is iterable.

An array is iterable, but not an iterator. You can get an iterator from an array, which is what makes it iterable:

// iterable
let arr = [1, 2, 3, 4]          

// Symbol.iterator function returns the iterator
let it = arr[Symbol.iterator]() 

// iterators have next()
console.log(it.next())          
console.log(it.next())          
console.log(it.next())          
console.log(it.next())          
console.log(it.next())
Mark
  • 90,562
  • 7
  • 108
  • 148
  • Amazing. That clarifies. Can you comment on that weird subscripting syntax? `arr[Symbol.iterator]()`? Why are we putting Symbol.iterator inside square brackets? – Tom Auger May 15 '19 at 15:51
  • 1
    @TomAuger -- [`Symbol.iterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator) is a [Symbol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol). The bracket notation is just a property lookup on the array returning a function using the key returned by Symbol.iterator. This allows the key to be guaranteed to be unique. Unlike something like a string `array["Iterator"]` that you could accidentally overwrite. JS has a handful of "Well-known symbols" built in — you can, of course, also create your own. – Mark May 15 '19 at 15:56
  • Posted a follow-up question to deepen my understanding of the reason for this particular implementation: https://stackoverflow.com/questions/56258471/jacascript-reason-behind-strange-iterator-syntax-on-arrays – Tom Auger May 22 '19 at 13:53
  • Clarification: `Array` in JavaScript is an `Iterable` that returns an `IterableIterator`, which then supports recursive resolution. Example: `[1,2,3][Symbol.iterator]()[Symbol.iterator]()[Symbol.iterator]()` – vitaly-t Dec 20 '21 at 13:58
1

Mark Meyer's answer helps you see the critical distinction between an iterable and an iterator.

If you want to make your own values iterable, you must implement Symbol.iterator -

class Squares {
  constructor (...values) {
    this.values = values
  }
  *[ Symbol.iterator ] () {
    for (const v of this.values)
      yield v * v
  }
}

const iterable =
  new Squares(1, 2, 3, 4, 5)

for (const v of iterable)
  console.log(v)
// 1
// 4
// 9
// 16
// 25

console.log(Array.from(iterable))
// [ 1, 4, 9, 16, 25 ]

Now see how we can get an iterator from an iterable -

class Squares {
  constructor (...values) {
    this.values = values
  }
  *[ Symbol.iterator ] () {
    for (const v of this.values)
      yield v * v
  }
}

const iterable =
  new Squares(1, 2, 3, 4, 5)

const iterator =
  iterable[Symbol.iterator]()

console.log(iterator.next()) // { value: 1, done: false }
console.log(iterator.next()) // { value: 4, done: false }
console.log(iterator.next()) // { value: 9, done: false }
console.log(iterator.next()) // { value: 16, done: false }
console.log(iterator.next()) // { value: 25, done: false }
console.log(iterator.next()) // { value: undefined, done: true }
Mulan
  • 129,518
  • 31
  • 228
  • 259
  • Instead of `*[Symbol.iterator](){` above, could one just create a more conventionally named method, like `*getIterator(){`? Or is there some behind-the-scenes "magic" associated with `Symbol.iterator` that makes it work under the hood? – Tom Auger May 22 '19 at 13:55
  • you could name the method `getIterator` but [Symbol.iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator) is used by convention in order to take advantage of other JavaScript features like `Array.from`, `for-of` syntax and more. See the docs on `Symbol.iterator` for more details. – Mulan May 22 '19 at 18:06