The issue is as VLAZ mentioned in their comment. To fully explore this, we need to understand reference vs. value types in JS.
Let's look at a simple program:
let a = 1;
let b = a;
a = 2;
console.log(b) // prints 1
This works because we're storing a simple, primitive type like a number. With more complex types like arrays and objects, we'll see different behavior.
let a = { foo: true, bar: true };
let b = a;
a.foo = false;
console.log(b) // prints { foo: false, bar: true }
This is because more complex types in JS like arrays and objects are passed by reference, not by value. In essence, this means that when we store an object using a variable, the variable is not storing the values, but rather a reference to the object's location in your machine's memory. Let's add comments to the above code for clarity.
// a stores a pointer to memory location A, where our object literal lives
let a = { foo: true, bar: true };
// b stores a pointer to memory location A
let b = a;
// change a value on the object in memory location A
a.foo = false;
// print the value in memory location A
console.log(b)
This reference vs value differentiation has to do with the complexities of managing memory when we have potentially large, dynamic objects, so JS does a bunch of low-level stuff for us behind the scenes and gives us a nice user-friendly syntax for instantiating a data type that would be more complicated to implement manually in a lower-level language. However, we still have to be aware of this difference.
The crux of your issue is that in each index of your array, you're storing a reference to the exact same object, so when you update one index, you're actually updating the value at the memory location the index is pointing to. Since all the indices are pointing to the same memory location, changing one behaves similarly to changing them all.
Fortunately, armed with this information we can simply devise a way to create a new object on each iteration, whether by copying the reference object or just passing an object literal to the Array.push()
method. Here's a potential solution.
let arr = [];
for (let i = 0; i < 100; i++) {
// this "object literal" is defined when push is called, creating a new object on each iteration
arr.push({
a: Math.floor(Math.random() * 2),
b: 5,
c: undefined
});
}