I do understand what happens there. But I want to visualize the whole steps.
Let me clarify:
list
and head
are stored across the stack with reference and pointed to the same object. okay.
when it performs list.next = { val: c, next: null}
, it updates the head like { val: 0, next: { val: 1, next: null }}
.
Then list = list.next
happens means now the list will be { val: 1, next: null }
.
Consecutively next, the head is updated to { val: 0, next: { val: 1, next: {val: 2, next: null}}}
.
At this point,
let head = { val: 0, next: null };
let list = head;
const arr = [1, 2, 3, 4];
for (let c of arr) {
list.next = { val: c, next: null };
list = list.next;
}
console.log(head);
The head is not getting updated to { val: 0, next: { val: 1, next: null }}
. The head reference never changes, and keeps referencing the same node. To make head
point to a different node, you'd have to make an assignment to head
, but that never happens after the first and only assignment to it.
On the other hand, list
does get assigned new references, which is to the last node that was created.
I want to visualize the whole steps.
OK. After the first two statements, where head
and list
are assigned, we have the following state:
head list
↓ ↓
┌────────────┐
│ value: 0 │
│ next: null │
└────────────┘
The two variables head
and list
reference the same node.
In the first iteration of the loop, c
is 1, and the following property assignment is executed -- this is a mutation of the node that was created above:
list.next = { val: c, next: null };
The effect is as follows:
head list
↓ ↓
┌────────────┐ ┌────────────┐
│ value: 0 │ │ value: 1 │
│ next: ────────►│ next: null │
└────────────┘ └────────────┘
The next statement is crucial: the variable list
gets a new reference. This is not a mutation, but a variable assignment:
list = list.next;
And so we get this:
head list
↓ ↓
┌────────────┐ ┌────────────┐
│ value: 0 │ │ value: 1 │
│ next: ────────►│ next: null │
└────────────┘ └────────────┘
Notice how head
and list
no longer reference the same node!
Let's do one more iteration of the loop, with c
equal to 2. First:
list.next = { val: c, next: null };
...leads to:
head list
↓ ↓
┌────────────┐ ┌────────────┐ ┌────────────┐
│ value: 0 │ │ value: 1 │ │ value: 2 │
│ next: ────────►│ next: ────────►│ next: null │
└────────────┘ └────────────┘ └────────────┘
And again list = list.next;
will assign only to the list
variable, so we get:
head list
↓ ↓
┌────────────┐ ┌────────────┐ ┌────────────┐
│ value: 0 │ │ value: 1 │ │ value: 2 │
│ next: ────────►│ next: ────────►│ next: null │
└────────────┘ └────────────┘ └────────────┘
The invariant here is that head
always references the first node of the list, and at the start (and end) of a loop's iteration, list
references the last node in the list.
I hope this clarifies it.