I am trying to understand a point of confusion I have with JavaScript objects. Specifically, I am interested in finding what, if anything, causes an object reference to break.
To demonstrate the phenomenon, I have included a copy of some output from Chrome's JavaScript console. Note that I am working with arrays here, but we would expect objects to behave similarly given the subtle distinction between arrays and objects in JS. I have added comments for clarity.
// Set x to some array literal
> x = [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
// Set y to x
> y = x
[1, 2, 3, 4, 5]
> x
[1, 2, 3, 4, 5] // as expected
> y
[1, 2, 3, 4, 5] // as expected
As demonstrated above, both x
and y
output the expected value. Now I shuffle the values of x using a function called shuffle
(specified at the bottom of this question).
// Shuffle x
> x = shuffle(x)
[5, 1, 4, 2, 3]
> x
[5, 1, 4, 2, 3] // x changes as expected
> y
[5, 1, 4, 2, 3] // y changes as expected
Again, everything works as expected above. The variables x
and y
have maintained reference to the same object. However, when we repeat this operation, the results are strange.
// Shuffle x
> x = shuffle(x)
[3, 1, 5, 4, 2]
> x
[3, 1, 5, 4, 2] // x changes as expected
> y
[5, 1, 4, 2, 3] // y didn't change this time
Below is the shuffle function, adapted from here. Its purpose is to shuffle the contents of an array (parameter r1
) and to return the first n
items of the mixed array.
function shuffle(r1,n) {
var i = r1.length, j, tempi, tempj, r2;
r2 = r1;
while (--i) {
j = Math.floor(Math.random() * (i + 1));
tempi = r2[i];
tempj = r2[j];
r2[i] = tempj;
r2[j] = tempi;
}
return r2.slice(0,n);
}
I have since fixed the problem by rewriting my shuffle function based on this function. However, I would still like to understand what's going on. For a quick look at the code in action, I have made a jsFiddle.
Any ideas? I appreciate your time.
If you remove the .slice(0,n);
, it will behave the way you expect. slice makes a new array.
So the first time you call shuffle, within your loop you modify the array x = y = r1 = r2
. Then you make a copy of it on that last line and assign that to x. Now x !== y
, but they contain the exact same elements. You can test that they are distinct objects after your first call to shuffle:.
The next time you call shuffle you are shuffling the copy of x
you made and y
is untouched.
.slice()
makes a shallow copy of the Array, and so you're overwriting x
with a new Array.
// The original was shuffled, but now `x` is a new Array
x = shuffle(x);
That's why y
showed the first shuffle (because you hadn't sliced it yet), but none thereafter. The subsequent shuffle was on the overwritten x
, and y
still references the original.
If you wanted to truncate the original Array, just change its .length
.
So instead of this:
return r2.slice(0,n);
Do this:
r2.length = n;
...though you're not passing anything to n
currently.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With