0

Consider the following code.

const iterable = [10, 20, 30];

for (let value of iterable) {
  value += 1;
  console.log(value);
}

// 11
// 21
// 31

console.log(('after for...of'), iterable); // [10, 20, 30] - no change

const iterable1 = [10, 20, 30];

for (let i = 0; i < iterable1.length; i++) {
  iterable1[i] += 1;
  console.log(iterable1[i]);
}

// 11
// 21
// 31

console.log(('after regular for...'), iterable1); // [11, 21, 31] - values changed

As you see and as was stated in the question, the values in the array didn't change after the for...of loop, but did after the regular for loop.

After reading this article on MDN about the for... of I was led to believe that(at least in this particular context) it would be identical to the for loop, but as you see, this has proven not to be the case. It is especially baffling for me since the article states that

It invokes a custom iteration hook with statements to be executed for the value of each distinct property of the object.

The article proceeds to state that you can reassign values of the iterable(in our case, an array) inside the block.

You can use let instead of const too, if you reassign the variable inside the block.

It would seem to me that I lack some critical (maybe even trivial) insight into how the for...of works, and I hope you could help me better understand what is going on.

Kekuy
  • 135
  • 5
  • You can think of `for...of` as a a normal for loop that does `let value = array[index]`. When you change `value` after it is being assigned `array[index]` the value in `array` doesn't change. – 3limin4t0r Aug 29 '20 at 14:30

3 Answers3

4

In your for ... of loop, on each iteration value is a copy of an element in the array. Thus,

value += 1;

is critically different from

iterable1[i] += 1;

The value += 1; statement does work, and after that value will indeed contain a number one greater than an element of the array. However, the element of the array itself remains unchanged.

Consider this simpler case:

let x = 0, y;

y = x;
y += 1;
console.log(x);

you would see no change to the value of x. The variable y is also just a copy of the value of x, so when y changes, x is unaffected.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • It's only a copy because the array is storing primitives right? If it were objects then value would be a reference, so modifying value in that case would effect the original array. – Shnick Aug 29 '20 at 14:14
  • 1
    @Shnick well the words to describe that situation get tricky. The variable is still a copy of the *actual* value (a reference to an object), so yes modifying the object it self will be reflected whether you use `value` or the array element to get to the object. Strictly speaking though, it's still a copy of the value. It's just that you can't do "interesting" things with object references that are like adding one to a number. – Pointy Aug 29 '20 at 14:16
  • See also: [Is JavaScript a pass-by-reference or pass-by-value language?](https://stackoverflow.com/questions/518000/is-javascript-a-pass-by-reference-or-pass-by-value-language) – 3limin4t0r Aug 29 '20 at 14:24
2

In the first case you are just accessing the value but after after increment you are not setting it back to the array. Since array index is not available while using for...of you can use entries

const iterable = [10, 20, 30];

for (const [i, v] of iterable.entries()) {
  iterable[i] = v + 1
}
console.log(('after for...of'), iterable);
brk
  • 48,835
  • 10
  • 56
  • 78
1

Might be helpful to look at a for...of loop as similar to a function.

Remember, in a function when you pass in an argument that is a primitive data-type, the parameter for that argument will receive a 'copy' of that argument's primitive value. Any changes made to the parameter will not effect the outside variable|argument.

And, when you pass in an argument that is an object, the parameter for that argument gets a copy of the address in memory of the object e.g. it will refer to the same object. Any changes made to the parameter will effect the outside variable|argument.

The code snippet below demonstrates these two ideas:

primitiveVariable = 1;
obj = {num: 1};

function f(primitive, object) {
  primitive+=1;
  object.num+=1;
  console.log(primitive);
  console.log(object)
}

f(primitiveVariable, obj);

console.log(primitiveVariable);
console.log(obj);

A for...of loop operates the same:

a = [1, 2, 3];

for (item of a) {
  item += 1;
}

console.log(a);

for (item of a) {
  a[0] = item;
}

console.log(a);
tonitone120
  • 1,920
  • 3
  • 8
  • 25