Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript object reference breaks after changing initial object's value

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.

like image 613
chrisfargen Avatar asked Mar 25 '23 04:03

chrisfargen


2 Answers

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.

like image 174
Paul Avatar answered Apr 12 '23 13:04

Paul


.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.

like image 35
user1106925 Avatar answered Apr 12 '23 15:04

user1106925