I have the following piece of code that defines an array, then an iterator object with a generator yields the value from this array and I output each value with the spread operator:
const arr = ['0', '1', '4', 'a', '9'];
const my_obj = {
[Symbol.iterator]: function*() {
for(let index of arr) {
yield `${index}`;
}
}
};
const all = [...my_obj]
console.log(...my_obj)
The result is:
0
1
4
a
9
What I don’t understand is how is the spread operator variable “...my_obj” getting the values of the array if “my_obj” is an object, not an array. From what I understand: “my_obj” is reciving an object and if you apply the spread operator it should get the “key:value”.
Could someone explain how did it obtain the values?
I have the following piece of code that defines an array, then an iterator object with a generator yields the value from this array and I output each value with the spread operator:
const arr = ['0', '1', '4', 'a', '9'];
const my_obj = {
[Symbol.iterator]: function*() {
for(let index of arr) {
yield `${index}`;
}
}
};
const all = [...my_obj]
console.log(...my_obj)
The result is:
0
1
4
a
9
What I don’t understand is how is the spread operator variable “...my_obj” getting the values of the array if “my_obj” is an object, not an array. From what I understand: “my_obj” is reciving an object and if you apply the spread operator it should get the “key:value”.
Could someone explain how did it obtain the values?
Share Improve this question edited Oct 23, 2024 at 5:35 VLAZ 29.1k9 gold badges63 silver badges84 bronze badges asked Sep 4, 2020 at 18:21 GoldMambaGoldMamba 211 silver badge5 bronze badges 2- 1 In javascript Array is not a type.. The type of an array is also an object – anees Commented Sep 4, 2020 at 18:41
- exactly what @anees said. cosole log typeof(arr) and it will print Object. – Souritra Das Gupta Commented Sep 4, 2020 at 19:03
3 Answers
Reset to default 8The spread operator and for...of
statements call the iterable protocol of an object. Some objects, like Array
, String
, Set
and Map
have built in iterable protocols. That means that they have the @@iterator
method.
You yourself just created an object and gave it a property of [Symbol.iterator]
. Now your object knows what to do when the spread syntax of for...of
is called on this object, which is to call this iterator and loop over an iterable object created by the generator function in the [Symbol.iterator]
key.
And in your generator function you've specified that on each iteration a value of the arr
should be yielded until that loop is plete.
MDN showns an example here where it says:
Some built-in constructs—such as the spread syntax—use the same iteration protocol under the hood:
console.log([...someString]); // ["h", "i"]
A pattern you will see sometimes is that an objects has a values
method. Most of the times this is actually a generator function. When the [Symbol.iterator]
is called it returns the generator function which then loops over the values in the object.
Down here I've created a little demo which takes in a string and loops over the letters with for...of
and lays them out with the spread operator. The [Symbol.iterator]
will call a generator function that looks up each letter's position in the alphabet. Both are using the same iterable protocol.
class LettersObjects {
*values() {
// Here we use the iterable protocol of a string.
for (const letter of this.letters) {
const position = this.alphabet.indexOf(letter.toLowerCase()) + 1;
yield position.toString().padStart(2, '0');
}
}
// This is called with for...of and ...spread.
[Symbol.iterator]() {
return this.values();
}
constructor(letters) {
this.letters = letters;
this.alphabet = 'abcdefghijklmnopqrstuvwxyz';
}
}
const letters = new LettersObjects('Hello world');
// Call the iterable protocol on our LetterObjects instance.
for (const letter of letters) {
console.log('loop:', letter);
}
// Call the iterable protocol on our LetterObjects instance.
console.log('Spread:', ...letters);
This happens because an object is an iterable.
The spread operator spreads iterables and not arrays specifically. it can spread objects, even strings for that matter.
Spread operator documentation
The spread operator allows an iterable such as an array expression or string to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected, or an object expression to be expanded in places where zero or more key-value pairs (for object literals) are expected.
We can easily grasp the concept of iterables by making one of our own.
For instance, we have an object that is not an array, but looks suitable for for..of.
- When for..of starts, it calls that method once (or errors if not found). The method must return an iterator – an object with the method next.
- Onward, for..of works only with that returned object.
- When for..of wants the next value, it calls next() on that object.
- The result of next() must have the form {done: Boolean, value: any}, where done=true means that the iteration is finished, otherwise value is the next value.
If the object satisfies for iterable, i.e. for..of loop. It can be spread using the spread operator and can be assigned in array because array is also an iterable.
Ref: iterables in javascript spread operator